@astrosheep/keiyaku 4.6.6 → 4.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/build/{integrations/marketplace → plugins}/.agents/plugins/marketplace.json +1 -1
- package/build/plugins/.claude-plugin/marketplace.json +5 -0
- package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/.claude-plugin/plugin.json +1 -1
- package/build/{integrations/marketplace/plugins/keiyaku → plugins/codex}/.codex-plugin/plugin.json +1 -1
- package/build/plugins/codex/skills/keiyaku/SKILL.md +44 -0
- package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +130 -0
- package/build/plugins/codex/skills/keiyaku-akuma/references/automation.md +269 -0
- package/build/plugins/codex/skills/keiyaku-babysit/SKILL.md +65 -0
- package/build/plugins/codex/skills/keiyaku-bind/SKILL.md +216 -0
- package/build/plugins/codex/skills/keiyaku-bind/agents/openai.yaml +4 -0
- package/build/plugins/codex/skills/keiyaku-task/SKILL.md +104 -0
- package/build/plugins/codex/skills/keiyaku-workflow/SKILL.md +129 -0
- package/build/plugins/opencode/package.json +6 -0
- package/build/plugins/opencode/skills/keiyaku/SKILL.md +44 -0
- package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +130 -0
- package/build/plugins/opencode/skills/keiyaku-akuma/references/automation.md +269 -0
- package/build/plugins/opencode/skills/keiyaku-babysit/SKILL.md +65 -0
- package/build/plugins/opencode/skills/keiyaku-bind/SKILL.md +216 -0
- package/build/plugins/opencode/skills/keiyaku-bind/agents/openai.yaml +4 -0
- package/build/plugins/opencode/skills/keiyaku-task/SKILL.md +104 -0
- package/build/plugins/opencode/skills/keiyaku-workflow/SKILL.md +129 -0
- package/build/skills/keiyaku/SKILL.md +44 -0
- package/build/skills/keiyaku-akuma/SKILL.md +130 -0
- package/build/skills/keiyaku-akuma/references/automation.md +269 -0
- package/build/skills/keiyaku-babysit/SKILL.md +65 -0
- package/build/skills/keiyaku-bind/SKILL.md +216 -0
- package/build/skills/keiyaku-bind/agents/openai.yaml +4 -0
- package/build/skills/keiyaku-task/SKILL.md +104 -0
- package/build/skills/keiyaku-workflow/SKILL.md +129 -0
- package/build/src/akuma/akuma-instance.js +7 -5
- package/build/src/akuma/akuma-observe.d.ts +4 -0
- package/build/src/akuma/akuma-observe.js +12 -0
- package/build/src/akuma/akuma-owner.d.ts +3 -2
- package/build/src/akuma/akuma-owner.js +28 -10
- package/build/src/akuma/akuma-selection-execution.d.ts +3 -1
- package/build/src/akuma/akuma-selection-execution.js +2 -1
- package/build/src/akuma/akuma-wait.d.ts +2 -1
- package/build/src/akuma/akuma-wait.js +4 -1
- package/build/src/akuma/body.d.ts +1 -0
- package/build/src/akuma/body.js +12 -23
- package/build/src/akuma/selection-owner-port.d.ts +1 -1
- package/build/src/akuma/selection-owner-port.js +16 -5
- package/build/src/akuma/selection-request.d.ts +2 -0
- package/build/src/akuma/selection-request.js +2 -1
- package/build/src/akuma-body.js +4 -2
- package/build/src/cli/commands/akuma.js +6 -12
- package/build/src/cli/commands/install.js +3 -3
- package/build/src/library/akuma-creation.js +21 -19
- package/build/src/library/selection.js +5 -2
- package/build/src/plugin/akuma-signals.d.ts +10 -11
- package/build/src/plugin/akuma-signals.js +36 -20
- package/build/src/plugin/public.d.ts +5 -0
- package/build/src/plugin/runtime.d.ts +1 -0
- package/build/src/plugin/runtime.js +34 -23
- package/package.json +7 -7
- package/build/integrations/marketplace/.claude-plugin/marketplace.json +0 -5
- package/build/integrations/marketplace/plugins/keiyaku/package.json +0 -17
- /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku/SKILL.md +0 -0
- /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-akuma/SKILL.md +0 -0
- /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-akuma/references/automation.md +0 -0
- /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-babysit/SKILL.md +0 -0
- /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-bind/SKILL.md +0 -0
- /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-bind/agents/openai.yaml +0 -0
- /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-task/SKILL.md +0 -0
- /package/build/{integrations/marketplace/plugins/keiyaku → plugins/claude}/skills/keiyaku-workflow/SKILL.md +0 -0
- /package/build/{integrations/marketplace/plugins/keiyaku/opencode.js → plugins/opencode/index.js} +0 -0
- /package/build/{integrations → plugins}/pi/keiyaku.ts +0 -0
|
@@ -64,7 +64,7 @@ export function decodeAskObservation(observation, schema) {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
export async function observeAdmittedAskAkuma(input) {
|
|
67
|
-
const owner = new AkumaOwner(input.id, input.path);
|
|
67
|
+
const owner = new AkumaOwner(input.id, input.path, input.outcomeObserved);
|
|
68
68
|
const tell = input.receipt ?? (await owner.admittedReceipt(input.tellId));
|
|
69
69
|
await input.onObserve?.admitted?.(tell, input.id);
|
|
70
70
|
let settled;
|
|
@@ -118,6 +118,7 @@ export async function executeAskAkuma(input) {
|
|
|
118
118
|
wake: admission.wake,
|
|
119
119
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
120
120
|
...(input.onObserve === undefined ? {} : { onObserve: input.onObserve }),
|
|
121
|
+
...(input.outcomeObserved === undefined ? {} : { outcomeObserved: input.outcomeObserved }),
|
|
121
122
|
});
|
|
122
123
|
}
|
|
123
124
|
export async function executeKillAkuma(input) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AkumaAlias } from "../identity/selector.js";
|
|
2
2
|
import type { WorldRoot } from "../world.js";
|
|
3
|
-
import { type WaitReason } from "./akuma-observe.js";
|
|
3
|
+
import { type OutcomeObserver, type WaitReason } from "./akuma-observe.js";
|
|
4
4
|
import type { AkumaStatus } from "./akuma.js";
|
|
5
5
|
import type { DispatchAssociation } from "./dispatch-association.js";
|
|
6
6
|
import type { ActivityRow } from "./projection.js";
|
|
@@ -33,6 +33,7 @@ type WaitExecutionInput = Readonly<{
|
|
|
33
33
|
signal?: AbortSignal;
|
|
34
34
|
/** Internal custom completion judge over the full final observation where supplied. */
|
|
35
35
|
predicate?: (status: AkumaStatus) => boolean;
|
|
36
|
+
outcomeObserved?: OutcomeObserver;
|
|
36
37
|
/** Resolves one Akuma's identity facts; consulted once per Akuma a viewer first sees. */
|
|
37
38
|
identity?: (id: AkumaStatus["id"]) => Promise<WaitIdentityFacts>;
|
|
38
39
|
/** The selected set in the caller's order; a viewer's head and scoreboard follow it. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AkumaNotBornError } from "./akuma-errors.js";
|
|
2
|
-
import { createLiveStatusReader, defaultWaitComplete, readLiveStatus, readWaitComplete, waitForObservation, } from "./akuma-observe.js";
|
|
2
|
+
import { createLiveStatusReader, defaultWaitComplete, readLiveStatus, readWaitComplete, observeOutcomeRows, observeStatusOutcome, waitForObservation, } from "./akuma-observe.js";
|
|
3
3
|
import { pathsForAkuId } from "./identity.js";
|
|
4
4
|
const SHARED_ORDINARY_BUDGET = 30;
|
|
5
5
|
async function observeWaitRound(path, ids, signal, readers) {
|
|
@@ -88,6 +88,9 @@ export async function executeWaitAkuma(input) {
|
|
|
88
88
|
input.onSelected(ordered.map((id) => ({ id, ...facts.get(id) })));
|
|
89
89
|
}
|
|
90
90
|
const observeRound = async (round) => {
|
|
91
|
+
await Promise.all(round.observations.map(({ status, rows }) => input.observe === undefined
|
|
92
|
+
? observeStatusOutcome(status, input.outcomeObserved)
|
|
93
|
+
: observeOutcomeRows(status.id, rows, input.outcomeObserved)));
|
|
91
94
|
if (input.observe === undefined)
|
|
92
95
|
return;
|
|
93
96
|
const observed = [];
|
|
@@ -93,6 +93,7 @@ type BodyRuntime = Readonly<{
|
|
|
93
93
|
world: WorldRoot;
|
|
94
94
|
externalCommands: Readonly<Record<string, ErasedRequestCommand>>;
|
|
95
95
|
}>;
|
|
96
|
+
export declare function recordPluginDiagnostic(paths: AkumaPaths, occurrence: string, error: unknown): Promise<void>;
|
|
96
97
|
export declare function bodyProcessInput(launch: BodyLaunch, bodyModuleUrl?: string): Promise<{
|
|
97
98
|
argv: string[];
|
|
98
99
|
cwd: string;
|
package/build/src/akuma/body.js
CHANGED
|
@@ -10,7 +10,7 @@ import { HeldAkumaLeash, breakBody, decidePendingTellDisposition, drainPendingTe
|
|
|
10
10
|
import { akumaPathsSchema, pathsForAkuId, worldRootForAkumaPaths } from "./identity.js";
|
|
11
11
|
import { admitCallInitialTell, } from "./call-initial-tell.js";
|
|
12
12
|
import { akumaExecutionEnvironment } from "./providers/execution-environment.js";
|
|
13
|
-
import {
|
|
13
|
+
import { emitPluginSignal } from "../plugin/akuma-signals.js";
|
|
14
14
|
import { World } from "../world.js";
|
|
15
15
|
import { resolveProviderExecution } from "./providers/index.js";
|
|
16
16
|
import { clearBodyRequestTransport, settleBodyRequests } from "./request-serve.js";
|
|
@@ -156,7 +156,7 @@ function boundedDiagnostic(error) {
|
|
|
156
156
|
const text = diagnostic(error);
|
|
157
157
|
return text.length <= 500 ? text : `${text.slice(0, 500)}...`;
|
|
158
158
|
}
|
|
159
|
-
async function recordPluginDiagnostic(paths, occurrence, error) {
|
|
159
|
+
export async function recordPluginDiagnostic(paths, occurrence, error) {
|
|
160
160
|
try {
|
|
161
161
|
await appendFile(paths.log, `plugin ${occurrence} failed: ${boundedDiagnostic(error)}\n`);
|
|
162
162
|
}
|
|
@@ -306,8 +306,7 @@ async function recoverBodyRequests(input) {
|
|
|
306
306
|
}
|
|
307
307
|
return false;
|
|
308
308
|
}
|
|
309
|
-
function turnOutcomeEmitter(launch, soul) {
|
|
310
|
-
let plugins;
|
|
309
|
+
function turnOutcomeEmitter(launch, soul, world) {
|
|
311
310
|
const reportDiagnostic = (message) => {
|
|
312
311
|
void recordPluginDiagnostic(launch.paths, "turn outcome", message);
|
|
313
312
|
};
|
|
@@ -318,11 +317,7 @@ function turnOutcomeEmitter(launch, soul) {
|
|
|
318
317
|
if (turn === null)
|
|
319
318
|
throw new Error(`Committed Turn ${turnSequence} is missing from Heart`);
|
|
320
319
|
const participants = await readTurnParticipants(launch.paths, turnSequence);
|
|
321
|
-
|
|
322
|
-
world: await World.at(worldRootForAkumaPaths(launch.paths)),
|
|
323
|
-
reportDiagnostic,
|
|
324
|
-
});
|
|
325
|
-
await (await plugins).emit({
|
|
320
|
+
await emitPluginSignal({ world, reportDiagnostic }, {
|
|
326
321
|
kind: "akuma.turn-outcome",
|
|
327
322
|
akumaId: soul.id,
|
|
328
323
|
bodySequence: turn.bodySequence,
|
|
@@ -333,7 +328,7 @@ function turnOutcomeEmitter(launch, soul) {
|
|
|
333
328
|
? { kind: "answered", text: outcome.answer }
|
|
334
329
|
: { kind: "failed", reason: outcome.diagnostic },
|
|
335
330
|
...(launch.completion?.contractId === undefined ? {} : { contractId: launch.completion.contractId }),
|
|
336
|
-
}
|
|
331
|
+
});
|
|
337
332
|
}
|
|
338
333
|
catch (error) {
|
|
339
334
|
await recordPluginDiagnostic(launch.paths, "turn outcome", error);
|
|
@@ -341,22 +336,16 @@ function turnOutcomeEmitter(launch, soul) {
|
|
|
341
336
|
},
|
|
342
337
|
};
|
|
343
338
|
}
|
|
344
|
-
function bodyEndEmitter(launch, soul) {
|
|
345
|
-
let plugins;
|
|
339
|
+
function bodyEndEmitter(launch, soul, world) {
|
|
346
340
|
const reportDiagnostic = (message) => {
|
|
347
341
|
void recordPluginDiagnostic(launch.paths, "body end", message);
|
|
348
342
|
};
|
|
349
|
-
return (bodySequence, end, diagnostic) =>
|
|
343
|
+
return async (bodySequence, end, diagnostic) => {
|
|
350
344
|
try {
|
|
351
345
|
const latestTurn = await readLatestTurnForBody(launch.paths, bodySequence);
|
|
352
346
|
const initiator = latestTurn?.initiator;
|
|
353
347
|
const participants = latestTurn === null ? [] : await readTurnParticipants(launch.paths, latestTurn.sequence);
|
|
354
|
-
|
|
355
|
-
world: await World.at(worldRootForAkumaPaths(launch.paths)),
|
|
356
|
-
reportDiagnostic,
|
|
357
|
-
});
|
|
358
|
-
const runtime = await plugins;
|
|
359
|
-
await runtime.emit({
|
|
348
|
+
await emitPluginSignal({ world, reportDiagnostic }, {
|
|
360
349
|
kind: "akuma.body-ended",
|
|
361
350
|
akumaId: soul.id,
|
|
362
351
|
bodySequence,
|
|
@@ -365,12 +354,12 @@ function bodyEndEmitter(launch, soul) {
|
|
|
365
354
|
...(participants.length === 0 ? {} : { participants }),
|
|
366
355
|
...(diagnostic === undefined ? {} : { diagnostic }),
|
|
367
356
|
...(launch.completion?.contractId === undefined ? {} : { contractId: launch.completion.contractId }),
|
|
368
|
-
}
|
|
357
|
+
});
|
|
369
358
|
}
|
|
370
359
|
catch (error) {
|
|
371
360
|
await recordPluginDiagnostic(launch.paths, "body end", error);
|
|
372
361
|
}
|
|
373
|
-
}
|
|
362
|
+
};
|
|
374
363
|
}
|
|
375
364
|
async function admitInitialCallTell(runtime, input) {
|
|
376
365
|
const paths = pathsForAkuId(runtime.world, input.id);
|
|
@@ -575,8 +564,8 @@ export async function driveAkumaBody(launch, adapter, runtimeInput = {}) {
|
|
|
575
564
|
const prepared = await prepareBodyExecution(launch, leash, runtime, adapter);
|
|
576
565
|
if (prepared === null)
|
|
577
566
|
return;
|
|
578
|
-
emitBodyEnd = bodyEndEmitter(launch, prepared.soul);
|
|
579
|
-
emitTurnOutcome = turnOutcomeEmitter(launch, prepared.soul);
|
|
567
|
+
emitBodyEnd = bodyEndEmitter(launch, prepared.soul, world);
|
|
568
|
+
emitTurnOutcome = turnOutcomeEmitter(launch, prepared.soul, world);
|
|
580
569
|
const body = await leash.recordBody(launch.paths, { leashTakenAt: runtime.now() });
|
|
581
570
|
bodySequence = body.sequence;
|
|
582
571
|
const supervisor = await BodySupervisor.open(launch.paths, body.sequence, leash);
|
|
@@ -9,4 +9,4 @@ import type { SelectionRequestPort } from "./selection-request.js";
|
|
|
9
9
|
* unaddressable member never starts; the executor's own access to the target is
|
|
10
10
|
* then the product act, never a second addressing probe.
|
|
11
11
|
*/
|
|
12
|
-
export declare function selectionRequestPort(world: WorldRoot): SelectionRequestPort;
|
|
12
|
+
export declare function selectionRequestPort(world: WorldRoot, reportDiagnostic: (message: string) => void): SelectionRequestPort;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { outcomePluginObserver } from "../plugin/akuma-signals.js";
|
|
1
2
|
import { requireBornAkuma } from "./akuma-probe.js";
|
|
2
3
|
import { executeAskAkuma, executeKillAkuma, executeTellAkuma } from "./akuma-selection-execution.js";
|
|
3
4
|
import { executeWaitAkuma } from "./akuma-wait.js";
|
|
@@ -11,26 +12,36 @@ import { akumaObservationOf } from "./selection-observation.js";
|
|
|
11
12
|
* unaddressable member never starts; the executor's own access to the target is
|
|
12
13
|
* then the product act, never a second addressing probe.
|
|
13
14
|
*/
|
|
14
|
-
export function selectionRequestPort(world) {
|
|
15
|
+
export function selectionRequestPort(world, reportDiagnostic) {
|
|
15
16
|
const prove = async (targets) => {
|
|
16
17
|
for (const id of targets)
|
|
17
18
|
await requireBornAkuma(world, id);
|
|
18
19
|
};
|
|
19
20
|
return {
|
|
20
|
-
wait: async ({ targets, ...request }) => {
|
|
21
|
+
wait: async ({ targets, observer, ...request }) => {
|
|
21
22
|
await prove(targets);
|
|
22
23
|
// The lower wait owner returns association-free native evidence; the
|
|
23
24
|
// actual request crossing composes its canonical transport shape.
|
|
24
|
-
const native = await executeWaitAkuma({
|
|
25
|
+
const native = await executeWaitAkuma({
|
|
26
|
+
path: world,
|
|
27
|
+
ids: targets,
|
|
28
|
+
...request,
|
|
29
|
+
outcomeObserved: outcomePluginObserver({ world, reportDiagnostic }, observer),
|
|
30
|
+
});
|
|
25
31
|
return { ...native, observations: native.observations.map(akumaObservationOf) };
|
|
26
32
|
},
|
|
27
33
|
tell: async ({ target, ...request }) => {
|
|
28
34
|
await prove([target]);
|
|
29
35
|
return await executeTellAkuma({ path: world, id: target, ...request });
|
|
30
36
|
},
|
|
31
|
-
ask: async ({ target, ...request }) => {
|
|
37
|
+
ask: async ({ target, observer, ...request }) => {
|
|
32
38
|
await prove([target]);
|
|
33
|
-
return await executeAskAkuma({
|
|
39
|
+
return await executeAskAkuma({
|
|
40
|
+
path: world,
|
|
41
|
+
id: target,
|
|
42
|
+
...request,
|
|
43
|
+
outcomeObserved: outcomePluginObserver({ world, reportDiagnostic }, observer),
|
|
44
|
+
});
|
|
34
45
|
},
|
|
35
46
|
kill: async ({ targets, ...request }) => {
|
|
36
47
|
await prove(targets);
|
|
@@ -106,6 +106,7 @@ export type SelectionRequestPort = Readonly<{
|
|
|
106
106
|
targets: readonly AkumaStatus["id"][];
|
|
107
107
|
completion: "any" | "all";
|
|
108
108
|
timeoutMs?: number;
|
|
109
|
+
observer: string;
|
|
109
110
|
signal: AbortSignal;
|
|
110
111
|
}>): Promise<AkumaWaitResult>;
|
|
111
112
|
tell(input: Readonly<{
|
|
@@ -126,6 +127,7 @@ export type SelectionRequestPort = Readonly<{
|
|
|
126
127
|
schemaJson?: string;
|
|
127
128
|
initiator?: string;
|
|
128
129
|
interrupt?: boolean;
|
|
130
|
+
observer: string;
|
|
129
131
|
signal: AbortSignal;
|
|
130
132
|
}>): Promise<AkumaAskResult>;
|
|
131
133
|
kill(input: Readonly<{
|
|
@@ -167,7 +167,7 @@ export function selectionRequestCommand(action, port) {
|
|
|
167
167
|
if (request.action === "akuma.wait") {
|
|
168
168
|
return {
|
|
169
169
|
kind: "served",
|
|
170
|
-
result: await port.wait({ ...request, signal: facts.signal }),
|
|
170
|
+
result: await port.wait({ ...request, observer: facts.requester, signal: facts.signal }),
|
|
171
171
|
service: { action: request.action },
|
|
172
172
|
};
|
|
173
173
|
}
|
|
@@ -196,6 +196,7 @@ export function selectionRequestCommand(action, port) {
|
|
|
196
196
|
body: request.body,
|
|
197
197
|
tellId: facts.id,
|
|
198
198
|
recordedAt: facts.admittedAt,
|
|
199
|
+
observer: facts.requester,
|
|
199
200
|
...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs }),
|
|
200
201
|
...(request.schemaJson === undefined ? {} : { schemaJson: request.schemaJson }),
|
|
201
202
|
...(request.initiator === undefined ? {} : { initiator: request.initiator }),
|
package/build/src/akuma-body.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { bodyLaunchSchema, LEASH_HELD_EXIT, runAkumaBody } from "./akuma/body.js";
|
|
3
|
+
import { bodyLaunchSchema, LEASH_HELD_EXIT, runAkumaBody, recordPluginDiagnostic, } from "./akuma/body.js";
|
|
4
4
|
import { worldRootForAkumaPaths } from "./akuma/identity.js";
|
|
5
5
|
import { composeRequestCommands } from "./akuma/request-wire.js";
|
|
6
6
|
import { localExecutionContext } from "./akuma/requests.js";
|
|
@@ -108,7 +108,9 @@ export async function externalRequestCommandsFor(launch, processConfiguration) {
|
|
|
108
108
|
const world = await World.prove(worldRootForAkumaPaths(launch.paths));
|
|
109
109
|
return {
|
|
110
110
|
world,
|
|
111
|
-
commands: composeRequestCommands(selectionRequestCommands(selectionRequestPort(world
|
|
111
|
+
commands: composeRequestCommands(selectionRequestCommands(selectionRequestPort(world, (message) => {
|
|
112
|
+
void recordPluginDiagnostic(launch.paths, "outcome observation", message);
|
|
113
|
+
})), contractRequestCommands(contractUpstream(processConfiguration)), taskMutationRequestCommands(taskMutationRequestPort())),
|
|
112
114
|
};
|
|
113
115
|
}
|
|
114
116
|
function bodyProcessConfiguration() {
|
|
@@ -2,7 +2,7 @@ import { CliUsageError, isBlankInput } from "../usage.js";
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { squareAssignedParticipantName } from "@astrosheep/square";
|
|
5
|
-
import {
|
|
5
|
+
import { emitPluginSignal } from "../../plugin/akuma-signals.js";
|
|
6
6
|
import { canonicalBirthCwd } from "../../akuma/call-input.js";
|
|
7
7
|
import { Schema } from "../../akuma/index.js";
|
|
8
8
|
import { executionChannel } from "../../akuma/requests.js";
|
|
@@ -73,17 +73,11 @@ async function inputInitiator(input) {
|
|
|
73
73
|
// The initiator still travels with the request either way.
|
|
74
74
|
if (!(await World.established(input.path)))
|
|
75
75
|
return { initiator };
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
reportDiagnostic: (message) => writeStderr(message),
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
catch (error) {
|
|
85
|
-
writeStderr(`! plugin initiation: ${error instanceof Error ? error.message : String(error)}`);
|
|
86
|
-
}
|
|
76
|
+
await emitPluginSignal({
|
|
77
|
+
world: input.path,
|
|
78
|
+
...(input.settings === undefined ? {} : { settings: input.settings }),
|
|
79
|
+
reportDiagnostic: (message) => writeStderr(message),
|
|
80
|
+
}, { kind: "akuma.initiating", initiator });
|
|
87
81
|
return { initiator };
|
|
88
82
|
}
|
|
89
83
|
function emitAnswer(body, exact) {
|
|
@@ -47,8 +47,8 @@ export function renderInstallHelp() {
|
|
|
47
47
|
return `${INSTALL_ROOT_PURPOSE}\n\n${usageLine(INSTALL_USAGE)}\n\n--all continues past failures; any failure exits 1.`;
|
|
48
48
|
}
|
|
49
49
|
const INSTALL_TIMEOUT_MS = 5 * 60 * 1000;
|
|
50
|
-
const ASSET_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../../
|
|
51
|
-
const
|
|
50
|
+
const ASSET_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "../../../plugins");
|
|
51
|
+
const OPENCODE_PLUGIN_ROOT = resolve(ASSET_ROOT, "opencode");
|
|
52
52
|
const PI_PACKAGE_SOURCE = "npm:@astrosheep/keiyaku";
|
|
53
53
|
function recipes(harness) {
|
|
54
54
|
switch (harness) {
|
|
@@ -64,7 +64,7 @@ function recipes(harness) {
|
|
|
64
64
|
["claude", "plugin", "update", "keiyaku@keiyaku", "--scope", "user"],
|
|
65
65
|
];
|
|
66
66
|
case "opencode":
|
|
67
|
-
return [["opencode", "plugin",
|
|
67
|
+
return [["opencode", "plugin", OPENCODE_PLUGIN_ROOT, "--global", "--force"]];
|
|
68
68
|
case "pi":
|
|
69
69
|
return [["pi", "install", PI_PACKAGE_SOURCE]];
|
|
70
70
|
}
|
|
@@ -13,7 +13,7 @@ import { moveAlias } from "../alias/index.js";
|
|
|
13
13
|
import { AuthorityCorruptionError } from "../core/facts/errors.js";
|
|
14
14
|
import { publishDispatch, readDispatch } from "../dispatch/index.js";
|
|
15
15
|
import { parseAkumaAlias } from "../identity/selector.js";
|
|
16
|
-
import {
|
|
16
|
+
import { emitPluginSignal, outcomePluginObserver } from "../plugin/akuma-signals.js";
|
|
17
17
|
import { World } from "../world.js";
|
|
18
18
|
import { addressAkuma } from "./address.js";
|
|
19
19
|
import { requireInput } from "./input.js";
|
|
@@ -108,9 +108,9 @@ function pluginDiagnostic(error) {
|
|
|
108
108
|
const message = error instanceof Error ? error.message : String(error);
|
|
109
109
|
return message.length <= 500 ? message : `${message.slice(0, 500)}...`;
|
|
110
110
|
}
|
|
111
|
-
async function recordPluginDiagnostic(paths, error) {
|
|
111
|
+
async function recordPluginDiagnostic(paths, error, occurrence = "call admission") {
|
|
112
112
|
try {
|
|
113
|
-
await appendFile(paths.log, `plugin
|
|
113
|
+
await appendFile(paths.log, `plugin ${occurrence} failed: ${pluginDiagnostic(error)}\n`);
|
|
114
114
|
}
|
|
115
115
|
catch {
|
|
116
116
|
// Plugin diagnostic loss must not change an already admitted call.
|
|
@@ -126,22 +126,16 @@ async function emitCalledSignal(input) {
|
|
|
126
126
|
try {
|
|
127
127
|
const paths = pathsForAkuId(input.path, input.admitted.id);
|
|
128
128
|
const callerAkumaId = input.admitted.requested ? await requestedParent(input.path, input.admitted.id) : undefined;
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
...(input.contractId === undefined ? {} : { contractId: input.contractId }),
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
await recordPluginDiagnostic(paths, error);
|
|
144
|
-
}
|
|
129
|
+
await emitPluginSignal({
|
|
130
|
+
world: input.path,
|
|
131
|
+
...(input.settings === undefined ? {} : { settings: input.settings }),
|
|
132
|
+
reportDiagnostic: (message) => void recordPluginDiagnostic(paths, message),
|
|
133
|
+
}, {
|
|
134
|
+
kind: "akuma.called",
|
|
135
|
+
akumaId: input.admitted.id,
|
|
136
|
+
...(callerAkumaId === undefined ? {} : { callerAkumaId }),
|
|
137
|
+
...(input.contractId === undefined ? {} : { contractId: input.contractId }),
|
|
138
|
+
});
|
|
145
139
|
}
|
|
146
140
|
catch {
|
|
147
141
|
// Plugin delivery must not change an already admitted call.
|
|
@@ -320,6 +314,13 @@ async function observeCall(input) {
|
|
|
320
314
|
tellId: input.initialTell.tellId,
|
|
321
315
|
timeoutMs: input.timeoutMs,
|
|
322
316
|
receipt: input.receipt,
|
|
317
|
+
outcomeObserved: outcomePluginObserver({
|
|
318
|
+
world: input.path,
|
|
319
|
+
...(input.settings === undefined ? {} : { settings: input.settings }),
|
|
320
|
+
reportDiagnostic: (message) => {
|
|
321
|
+
void recordPluginDiagnostic(pathsForAkuId(input.path, input.akuma), message, "outcome observation");
|
|
322
|
+
},
|
|
323
|
+
}),
|
|
323
324
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
324
325
|
onObserve,
|
|
325
326
|
});
|
|
@@ -400,6 +401,7 @@ export async function callAkumas(input, execution = localExecutionContext()) {
|
|
|
400
401
|
initialTell: parsed.initialTell,
|
|
401
402
|
receipt,
|
|
402
403
|
timeoutMs: parsed.timeoutMs,
|
|
404
|
+
...(parsed.settings === undefined ? {} : { settings: parsed.settings }),
|
|
403
405
|
...(parsed.signal === undefined ? {} : { signal: parsed.signal }),
|
|
404
406
|
...(parsed.observe === undefined ? {} : { observe: parsed.observe }),
|
|
405
407
|
});
|
|
@@ -13,6 +13,7 @@ import { observeDispatchAssociations } from "../dispatch/index.js";
|
|
|
13
13
|
import { observeContractAt } from "../git/observe.js";
|
|
14
14
|
import { withGitDecodeChannel } from "../git/read-observation.js";
|
|
15
15
|
import { observeCreatedTaskObservations } from "../task/created-observation.js";
|
|
16
|
+
import { outcomePluginObserver } from "../plugin/akuma-signals.js";
|
|
16
17
|
import { addressAkuma, addressAkumaSet, resolveAkuma, resolveAkumaSet, } from "./address.js";
|
|
17
18
|
import { requireInput } from "./input.js";
|
|
18
19
|
import { scopeForRepo } from "./repo.js";
|
|
@@ -77,6 +78,7 @@ function localSelection() {
|
|
|
77
78
|
const result = await executeAskAkuma({
|
|
78
79
|
path: addressed.path,
|
|
79
80
|
id: addressed.id,
|
|
81
|
+
outcomeObserved: outcomePluginObserver({ world: addressed.path, reportDiagnostic: console.error }),
|
|
80
82
|
body: input.body,
|
|
81
83
|
...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
|
|
82
84
|
...(input.schema === undefined ? {} : { schemaJson: schemaJsonText(input.schema) }),
|
|
@@ -239,7 +241,7 @@ function setAddress(values) {
|
|
|
239
241
|
export async function statusAkuma(input) {
|
|
240
242
|
const addressed = await addressAkuma(input);
|
|
241
243
|
try {
|
|
242
|
-
return await observeAkuma(await new AkumaOwner(addressed.id, addressed.path).status(), addressed.path, input.repo);
|
|
244
|
+
return await observeAkuma(await new AkumaOwner(addressed.id, addressed.path, outcomePluginObserver({ world: addressed.path, reportDiagnostic: console.error })).status(), addressed.path, input.repo);
|
|
243
245
|
}
|
|
244
246
|
catch (error) {
|
|
245
247
|
throw observationError(addressed.id, error);
|
|
@@ -268,6 +270,7 @@ async function localWait(addressed, input) {
|
|
|
268
270
|
path: addressed.path,
|
|
269
271
|
ids: addressed.orderedIds,
|
|
270
272
|
completion: input.completion,
|
|
273
|
+
outcomeObserved: outcomePluginObserver({ world: addressed.path, reportDiagnostic: console.error }),
|
|
271
274
|
...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
|
|
272
275
|
...(input.signal === undefined ? {} : { signal: input.signal }),
|
|
273
276
|
identity: waitIdentityFacts(addressed.path, input.repo, addressed.orderedIds),
|
|
@@ -390,7 +393,7 @@ export async function historyAkuma(input) {
|
|
|
390
393
|
validateHistoryInput(values);
|
|
391
394
|
const addressed = await addressAkuma(directAddress(values));
|
|
392
395
|
try {
|
|
393
|
-
const handle = new AkumaOwner(addressed.id, addressed.path);
|
|
396
|
+
const handle = new AkumaOwner(addressed.id, addressed.path, outcomePluginObserver({ world: addressed.path, reportDiagnostic: console.error }));
|
|
394
397
|
const contract = (await dispatchAssociations(values.repo, [addressed.id])).get(addressed.id);
|
|
395
398
|
if (values.last === true) {
|
|
396
399
|
const answer = await handle.lastAnswer();
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import type { Settings } from "../settings.js";
|
|
2
2
|
import type { WorldRoot } from "../world.js";
|
|
3
|
-
type
|
|
3
|
+
import type { PluginSignal } from "./public.js";
|
|
4
|
+
type PluginSignalEmission = Readonly<{
|
|
4
5
|
world: WorldRoot;
|
|
5
6
|
settings?: Settings;
|
|
6
7
|
reportDiagnostic: (message: string) => void;
|
|
7
|
-
akumaId: string;
|
|
8
|
-
callerAkumaId?: string;
|
|
9
|
-
contractId?: string;
|
|
10
8
|
}>;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The one producer entry for plugin signals. It owns runtime acquisition, the
|
|
11
|
+
* World Settings fallback, bounded delivery, and failure isolation: a delivery
|
|
12
|
+
* problem reaches the producer only as a diagnostic, never as a thrown error.
|
|
13
|
+
*/
|
|
14
|
+
export declare function emitPluginSignal(emission: PluginSignalEmission, signal: PluginSignal): Promise<void>;
|
|
15
|
+
/** One public observer operation, never shared with incidental owner reads. */
|
|
16
|
+
export declare function outcomePluginObserver(emission: PluginSignalEmission, observer?: string): (akumaId: string, historyIds: readonly string[]) => Promise<void>;
|
|
18
17
|
export {};
|
|
@@ -1,22 +1,38 @@
|
|
|
1
|
-
import { pluginRuntime } from "./runtime.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { pluginRuntime, reportPluginDiagnostic } from "./runtime.js";
|
|
2
|
+
/**
|
|
3
|
+
* The one producer entry for plugin signals. It owns runtime acquisition, the
|
|
4
|
+
* World Settings fallback, bounded delivery, and failure isolation: a delivery
|
|
5
|
+
* problem reaches the producer only as a diagnostic, never as a thrown error.
|
|
6
|
+
*/
|
|
7
|
+
export async function emitPluginSignal(emission, signal) {
|
|
8
|
+
try {
|
|
9
|
+
const runtime = await pluginRuntime({
|
|
10
|
+
world: emission.world,
|
|
11
|
+
...(emission.settings === undefined ? {} : { settings: emission.settings }),
|
|
12
|
+
reportDiagnostic: emission.reportDiagnostic,
|
|
13
|
+
});
|
|
14
|
+
await runtime.emit(signal, emission.reportDiagnostic);
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
reportPluginDiagnostic(emission.reportDiagnostic, "runtime", "signal", error);
|
|
18
|
+
}
|
|
9
19
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/** One public observer operation, never shared with incidental owner reads. */
|
|
21
|
+
export function outcomePluginObserver(emission, observer) {
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
return async (akumaId, historyIds) => {
|
|
24
|
+
const fresh = historyIds.filter((historyId) => {
|
|
25
|
+
const key = `${akumaId}:${historyId}`;
|
|
26
|
+
if (seen.has(key))
|
|
27
|
+
return false;
|
|
28
|
+
seen.add(key);
|
|
29
|
+
return true;
|
|
30
|
+
});
|
|
31
|
+
await Promise.all(fresh.map(async (historyId) => await emitPluginSignal(emission, {
|
|
32
|
+
kind: "akuma.outcome-observed",
|
|
33
|
+
akumaId,
|
|
34
|
+
historyId,
|
|
35
|
+
...(observer === undefined ? {} : { observer }),
|
|
36
|
+
})));
|
|
37
|
+
};
|
|
22
38
|
}
|
|
@@ -24,6 +24,11 @@ export type PluginSignalMap = Readonly<{
|
|
|
24
24
|
callerAkumaId?: string;
|
|
25
25
|
contractId?: string;
|
|
26
26
|
}>;
|
|
27
|
+
"akuma.outcome-observed": Readonly<{
|
|
28
|
+
akumaId: string;
|
|
29
|
+
historyId: string;
|
|
30
|
+
observer?: string;
|
|
31
|
+
}>;
|
|
27
32
|
"akuma.turn-outcome": Readonly<{
|
|
28
33
|
akumaId: string;
|
|
29
34
|
bodySequence: number;
|
|
@@ -10,5 +10,6 @@ type PluginRuntimeInput = Readonly<{
|
|
|
10
10
|
export type PluginRuntime = Readonly<{
|
|
11
11
|
emit(signal: PluginSignal, reportDiagnostic?: PluginDiagnostic): Promise<void>;
|
|
12
12
|
}>;
|
|
13
|
+
export declare function reportPluginDiagnostic(report: PluginDiagnostic | undefined, subject: string, stage: string, error: unknown): void;
|
|
13
14
|
export declare function pluginRuntime(input: PluginRuntimeInput): Promise<PluginRuntime>;
|
|
14
15
|
export {};
|