@astrosheep/keiyaku 4.0.0 → 4.0.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 +133 -8
- package/build/integrations/marketplace/plugins/keiyaku/.claude-plugin/plugin.json +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/.codex-plugin/plugin.json +4 -4
- package/build/integrations/marketplace/plugins/keiyaku/opencode.js +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/package.json +2 -2
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku/SKILL.md +13 -5
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-akuma/SKILL.md +91 -32
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/SKILL.md +134 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-bind/agents/openai.yaml +4 -0
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-task/SKILL.md +41 -6
- package/build/integrations/marketplace/plugins/keiyaku/skills/keiyaku-workflow/SKILL.md +262 -46
- package/build/src/akuma/abort.d.ts +2 -0
- package/build/src/akuma/abort.js +50 -0
- package/build/src/akuma/akuma.d.ts +50 -33
- package/build/src/akuma/akuma.js +175 -127
- package/build/src/akuma/archetype.d.ts +14 -5
- package/build/src/akuma/archetype.js +85 -39
- package/build/src/akuma/body.d.ts +3 -5
- package/build/src/akuma/body.js +490 -183
- package/build/src/akuma/coordinate.d.ts +9 -0
- package/build/src/akuma/coordinate.js +22 -0
- package/build/src/akuma/heart/facts.d.ts +88 -45
- package/build/src/akuma/heart/facts.js +19 -8
- package/build/src/akuma/heart/index.d.ts +64 -55
- package/build/src/akuma/heart/index.js +172 -252
- package/build/src/akuma/heart/rows.d.ts +38 -83
- package/build/src/akuma/heart/rows.js +117 -155
- package/build/src/akuma/heart/schema.d.ts +1 -1
- package/build/src/akuma/heart/schema.js +61 -22
- package/build/src/akuma/heart/soul.d.ts +8 -0
- package/build/src/akuma/heart/soul.js +136 -0
- package/build/src/akuma/heart/storage.d.ts +47 -0
- package/build/src/akuma/heart/storage.js +224 -0
- package/build/src/akuma/heart/tells.d.ts +12 -0
- package/build/src/akuma/heart/tells.js +79 -0
- package/build/src/akuma/heart/timeline.d.ts +15 -0
- package/build/src/akuma/heart/timeline.js +86 -0
- package/build/src/akuma/identity.d.ts +2 -2
- package/build/src/akuma/identity.js +9 -9
- package/build/src/akuma/index.d.ts +10 -3
- package/build/src/akuma/index.js +7 -1
- package/build/src/akuma/projection.d.ts +176 -0
- package/build/src/akuma/projection.js +256 -0
- package/build/src/akuma/provider-recipe.d.ts +24 -0
- package/build/src/akuma/provider-recipe.js +100 -0
- package/build/src/akuma/provider.d.ts +67 -13
- package/build/src/akuma/provider.js +172 -105
- package/build/src/akuma/providers/acp/core.d.ts +19 -0
- package/build/src/akuma/providers/acp/core.js +159 -0
- package/build/src/akuma/providers/acp/events.d.ts +20 -0
- package/build/src/akuma/providers/acp/events.js +73 -0
- package/build/src/akuma/providers/acp/index.d.ts +12 -0
- package/build/src/akuma/providers/acp/index.js +94 -0
- package/build/src/akuma/providers/{claude.d.ts → claude/events.d.ts} +9 -41
- package/build/src/akuma/providers/claude/events.js +238 -0
- package/build/src/akuma/providers/claude/index.d.ts +47 -0
- package/build/src/akuma/providers/claude/index.js +279 -0
- package/build/src/akuma/providers/claude/input.d.ts +13 -0
- package/build/src/akuma/providers/claude/input.js +109 -0
- package/build/src/akuma/providers/codex-app-server/events.d.ts +1 -1
- package/build/src/akuma/providers/codex-app-server/events.js +15 -12
- package/build/src/akuma/providers/codex-app-server/index.d.ts +15 -13
- package/build/src/akuma/providers/codex-app-server/index.js +77 -33
- package/build/src/akuma/providers/grok-build/index.d.ts +4 -0
- package/build/src/akuma/providers/grok-build/index.js +70 -0
- package/build/src/akuma/providers/index.d.ts +5 -3
- package/build/src/akuma/providers/index.js +35 -56
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +54 -0
- package/build/src/akuma/providers/opencode-sdk/events.js +235 -0
- package/build/src/akuma/providers/opencode-sdk/index.d.ts +33 -0
- package/build/src/akuma/providers/opencode-sdk/index.js +308 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +26 -0
- package/build/src/akuma/providers/opencode-sdk/session.js +63 -0
- package/build/src/akuma/providers/pi/events.d.ts +33 -0
- package/build/src/akuma/providers/pi/events.js +166 -0
- package/build/src/akuma/providers/pi/index.d.ts +11 -0
- package/build/src/akuma/providers/pi/index.js +191 -0
- package/build/src/akuma/publication.d.ts +3 -3
- package/build/src/akuma/publication.js +28 -40
- package/build/src/akuma/requests.d.ts +9 -6
- package/build/src/akuma/requests.js +95 -65
- package/build/src/alias/index.d.ts +4 -3
- package/build/src/alias/index.js +12 -13
- package/build/src/body/decode.js +7 -1
- package/build/src/body/region.d.ts +2 -0
- package/build/src/body/region.js +77 -7
- package/build/src/body/render.js +2 -1
- package/build/src/body/shape.js +7 -1
- package/build/src/body/verification.js +19 -4
- package/build/src/cli/accepted.d.ts +18 -12
- package/build/src/cli/accepted.js +73 -34
- package/build/src/cli/actor.js +3 -3
- package/build/src/cli/commands/akuma-invoke.d.ts +44 -19
- package/build/src/cli/commands/akuma-invoke.js +101 -52
- package/build/src/cli/commands/akuma.d.ts +27 -9
- package/build/src/cli/commands/akuma.js +151 -80
- package/build/src/cli/commands/contract.d.ts +155 -0
- package/build/src/cli/commands/contract.js +20 -0
- package/build/src/cli/commands/install.js +2 -0
- package/build/src/cli/commands/task-invoke.d.ts +19 -4
- package/build/src/cli/commands/task-invoke.js +65 -17
- package/build/src/cli/commands/task-query.d.ts +5 -0
- package/build/src/cli/commands/task-query.js +247 -0
- package/build/src/cli/commands/task.d.ts +3 -1
- package/build/src/cli/commands/task.js +52 -14
- package/build/src/cli/coordinates.d.ts +18 -0
- package/build/src/cli/coordinates.js +120 -0
- package/build/src/cli/draft.d.ts +8 -0
- package/build/src/cli/draft.js +96 -0
- package/build/src/cli/invoke.d.ts +9 -3
- package/build/src/cli/invoke.js +287 -108
- package/build/src/cli/main.js +54 -7
- package/build/src/cli/parse.d.ts +21 -117
- package/build/src/cli/parse.js +79 -84
- package/build/src/cli/render/akuma-tool-command.d.ts +6 -0
- package/build/src/cli/render/akuma-tool-command.js +144 -0
- package/build/src/cli/render/akuma-tool.d.ts +4 -2
- package/build/src/cli/render/akuma-tool.js +35 -5
- package/build/src/cli/render/akuma.d.ts +3 -1
- package/build/src/cli/render/akuma.js +247 -212
- package/build/src/cli/render/audit.d.ts +3 -0
- package/build/src/cli/render/audit.js +104 -0
- package/build/src/cli/render/catalog.d.ts +2 -0
- package/build/src/cli/render/catalog.js +16 -0
- package/build/src/cli/render/contract.d.ts +3 -3
- package/build/src/cli/render/contract.js +200 -54
- package/build/src/cli/render/kanshi.js +272 -129
- package/build/src/cli/render/receipt.d.ts +19 -0
- package/build/src/cli/render/receipt.js +106 -0
- package/build/src/cli/render/refusal.d.ts +16 -2
- package/build/src/cli/render/refusal.js +88 -3
- package/build/src/cli/render/region.d.ts +2 -0
- package/build/src/cli/render/region.js +17 -0
- package/build/src/cli/render/task.d.ts +2 -1
- package/build/src/cli/render/task.js +223 -60
- package/build/src/cli/render/terminal.d.ts +20 -0
- package/build/src/cli/render/terminal.js +121 -0
- package/build/src/cli/render/text.js +11 -3
- package/build/src/cli/result.d.ts +126 -19
- package/build/src/cli/usage.d.ts +1 -0
- package/build/src/cli/usage.js +3 -0
- package/build/src/contract-worktree.d.ts +44 -0
- package/build/src/contract-worktree.js +251 -0
- package/build/src/coordination/durable-file.d.ts +5 -1
- package/build/src/coordination/durable-file.js +57 -10
- package/build/src/coordination/sqlite-transaction-lock.d.ts +4 -0
- package/build/src/coordination/sqlite-transaction-lock.js +22 -5
- package/build/src/core/facts/codec.js +23 -4
- package/build/src/core/facts/eligibility.d.ts +1 -10
- package/build/src/core/facts/eligibility.js +0 -49
- package/build/src/core/facts/fold.js +1 -6
- package/build/src/core/facts/gate.d.ts +1 -0
- package/build/src/core/facts/gate.js +1 -0
- package/build/src/core/facts/observation.d.ts +3 -0
- package/build/src/core/facts/observation.js +12 -0
- package/build/src/core/facts/types.d.ts +10 -3
- package/build/src/core/subject.js +2 -2
- package/build/src/core/verbs/amend.d.ts +1 -1
- package/build/src/core/verbs/amend.js +3 -18
- package/build/src/core/verbs/bind.js +2 -16
- package/build/src/core/verbs/deliver.d.ts +1 -1
- package/build/src/core/verbs/deliver.js +14 -4
- package/build/src/core/verbs/placement.d.ts +1 -1
- package/build/src/core/verbs/placement.js +7 -9
- package/build/src/dispatch/index.d.ts +5 -3
- package/build/src/dispatch/index.js +30 -21
- package/build/src/duration.d.ts +10 -0
- package/build/src/duration.js +22 -0
- package/build/src/git/admission.d.ts +2 -2
- package/build/src/git/admission.js +33 -23
- package/build/src/git/hooks.d.ts +15 -0
- package/build/src/git/hooks.js +70 -18
- package/build/src/git/identity.d.ts +5 -1
- package/build/src/git/identity.js +23 -4
- package/build/src/git/integration.d.ts +48 -0
- package/build/src/git/integration.js +250 -0
- package/build/src/git/observe.d.ts +29 -15
- package/build/src/git/observe.js +185 -76
- package/build/src/git/read-observation.d.ts +41 -0
- package/build/src/git/read-observation.js +361 -0
- package/build/src/git/reconcile.d.ts +17 -5
- package/build/src/git/reconcile.js +305 -109
- package/build/src/git/repository.d.ts +41 -18
- package/build/src/git/repository.js +220 -114
- package/build/src/git/scratch.d.ts +17 -0
- package/build/src/git/scratch.js +73 -0
- package/build/src/git/target-placement.d.ts +92 -0
- package/build/src/git/target-placement.js +375 -0
- package/build/src/git/tender.d.ts +49 -0
- package/build/src/git/tender.js +63 -0
- package/build/src/git/terminal-seal.d.ts +24 -0
- package/build/src/git/terminal-seal.js +89 -0
- package/build/src/git/tree.d.ts +2 -2
- package/build/src/git/tree.js +4 -0
- package/build/src/git/workspace.d.ts +53 -0
- package/build/src/git/workspace.js +139 -0
- package/build/src/identity/selector.d.ts +6 -0
- package/build/src/identity/selector.js +19 -0
- package/build/src/index.d.ts +4 -2
- package/build/src/index.js +2 -1
- package/build/src/kanshi/index.d.ts +2 -2
- package/build/src/kanshi/index.js +1 -1
- package/build/src/kanshi/read.d.ts +7 -3
- package/build/src/kanshi/read.js +245 -45
- package/build/src/kanshi/report.d.ts +81 -6
- package/build/src/kanshi/select.d.ts +5 -0
- package/build/src/kanshi/select.js +33 -1
- package/build/src/library/address.d.ts +57 -0
- package/build/src/library/address.js +165 -0
- package/build/src/library/akuma-creation.d.ts +16 -7
- package/build/src/library/akuma-creation.js +96 -22
- package/build/src/library/audit.d.ts +18 -0
- package/build/src/library/audit.js +44 -0
- package/build/src/library/bind.d.ts +21 -0
- package/build/src/library/bind.js +56 -0
- package/build/src/library/catalog.d.ts +58 -0
- package/build/src/library/catalog.js +69 -0
- package/build/src/library/configuration.d.ts +6 -15
- package/build/src/library/configuration.js +14 -17
- package/build/src/library/contract.d.ts +23 -49
- package/build/src/library/contract.js +171 -185
- package/build/src/library/delivery.d.ts +24 -0
- package/build/src/library/delivery.js +21 -0
- package/build/src/library/fleet.d.ts +65 -0
- package/build/src/library/fleet.js +153 -0
- package/build/src/library/input.d.ts +3 -0
- package/build/src/library/input.js +19 -0
- package/build/src/library/keiyaku.d.ts +18 -3
- package/build/src/library/keiyaku.js +12 -1
- package/build/src/library/mutation.d.ts +39 -0
- package/build/src/library/mutation.js +37 -0
- package/build/src/library/reconcile.d.ts +29 -0
- package/build/src/library/reconcile.js +179 -0
- package/build/src/library/refusal.d.ts +21 -0
- package/build/src/library/refusal.js +29 -0
- package/build/src/library/region.d.ts +14 -1
- package/build/src/library/region.js +18 -4
- package/build/src/library/repo.d.ts +4 -13
- package/build/src/library/repo.js +14 -24
- package/build/src/protocol/attempt.d.ts +16 -3
- package/build/src/protocol/attempt.js +41 -10
- package/build/src/protocol/bind.d.ts +9 -2
- package/build/src/protocol/bind.js +48 -40
- package/build/src/protocol/intent.d.ts +57 -20
- package/build/src/protocol/intent.js +55 -79
- package/build/src/protocol/operations.d.ts +152 -43
- package/build/src/protocol/operations.js +482 -166
- package/build/src/protocol/outcome.d.ts +14 -3
- package/build/src/protocol/outcome.js +11 -3
- package/build/src/protocol/placement.d.ts +41 -0
- package/build/src/protocol/placement.js +147 -0
- package/build/src/protocol/read/documents.d.ts +2 -2
- package/build/src/protocol/read/documents.js +4 -4
- package/build/src/protocol/read/status.d.ts +18 -5
- package/build/src/protocol/read/status.js +73 -13
- package/build/src/protocol/run.d.ts +49 -7
- package/build/src/protocol/run.js +53 -25
- package/build/src/runtime/proc/line-rpc.d.ts +4 -8
- package/build/src/runtime/proc/line-rpc.js +22 -28
- package/build/src/runtime/proc/run.d.ts +23 -21
- package/build/src/runtime/proc/run.js +123 -89
- package/build/src/runtime/proc/stdio.d.ts +18 -0
- package/build/src/runtime/proc/stdio.js +58 -0
- package/build/src/settings.d.ts +7 -1
- package/build/src/settings.js +32 -17
- package/build/src/settlement/fence.d.ts +5 -0
- package/build/src/settlement/fence.js +14 -0
- package/build/src/settlement/holder.d.ts +35 -4
- package/build/src/settlement/holder.js +127 -28
- package/build/src/settlement/settle.d.ts +9 -1
- package/build/src/settlement/settle.js +74 -37
- package/build/src/task/board.d.ts +23 -5
- package/build/src/task/board.js +17 -10
- package/build/src/task/compose.d.ts +2 -2
- package/build/src/task/compose.js +18 -10
- package/build/src/task/context.d.ts +3 -3
- package/build/src/task/context.js +15 -20
- package/build/src/task/document.d.ts +2 -1
- package/build/src/task/document.js +15 -5
- package/build/src/task/index.d.ts +27 -15
- package/build/src/task/index.js +57 -35
- package/build/src/task/operations.d.ts +38 -13
- package/build/src/task/operations.js +80 -46
- package/build/src/task/query.d.ts +67 -0
- package/build/src/task/query.js +279 -0
- package/build/src/task/store.d.ts +5 -7
- package/build/src/task/store.js +43 -28
- package/build/src/verification/declaration.d.ts +1 -0
- package/build/src/verification/execution.d.ts +51 -0
- package/build/src/verification/execution.js +108 -0
- package/build/src/workspace-place.d.ts +41 -0
- package/build/src/workspace-place.js +386 -0
- package/build/src/world.d.ts +24 -0
- package/build/src/world.js +136 -0
- package/package.json +8 -4
- package/build/src/akuma/activity.d.ts +0 -66
- package/build/src/akuma/activity.js +0 -139
- package/build/src/akuma/providers/claude.js +0 -305
- package/build/src/context-root.d.ts +0 -4
- package/build/src/context-root.js +0 -15
- package/build/src/git/delivery.d.ts +0 -20
- package/build/src/git/delivery.js +0 -109
- package/build/src/git/verification.d.ts +0 -14
- package/build/src/git/verification.js +0 -56
- package/build/src/protocol/read/audit.d.ts +0 -25
- package/build/src/protocol/read/audit.js +0 -39
- package/build/src/verification/producer.d.ts +0 -23
- package/build/src/verification/producer.js +0 -54
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type ProviderAdapter, type Session } from "../../provider.js";
|
|
2
|
+
import type { ResumeCoordinate } from "../../heart/index.js";
|
|
3
|
+
import type { ProviderExecution, ProviderOptions } from "../../provider-recipe.js";
|
|
4
|
+
import { type OpencodeSdkLoader } from "./session.js";
|
|
5
|
+
export type OpencodeProviderTestOptions = Readonly<{
|
|
6
|
+
loader?: OpencodeSdkLoader;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function createOpencodeProvider(input?: ProviderExecution | OpencodeProviderTestOptions): ProviderAdapter;
|
|
9
|
+
export declare const opencodeProvider: Readonly<{
|
|
10
|
+
confinement(input: Readonly<{
|
|
11
|
+
cwd: string;
|
|
12
|
+
options: ProviderOptions;
|
|
13
|
+
}>): import("../../heart/facts.js").Confinement;
|
|
14
|
+
admitOptions(options: ProviderOptions): import("../../provider.js").ProviderOptionAdmission;
|
|
15
|
+
fork?(input: Readonly<{
|
|
16
|
+
session: ResumeCoordinate;
|
|
17
|
+
at: string;
|
|
18
|
+
cwd: string;
|
|
19
|
+
}>): Promise<Readonly<{
|
|
20
|
+
session: ResumeCoordinate;
|
|
21
|
+
}>>;
|
|
22
|
+
start(input: import("../../provider.js").DriveInput & Readonly<{
|
|
23
|
+
session: Readonly<{
|
|
24
|
+
kind: "fresh";
|
|
25
|
+
}>;
|
|
26
|
+
}>): Promise<Session>;
|
|
27
|
+
resume?(input: import("../../provider.js").DriveInput & Readonly<{
|
|
28
|
+
session: Readonly<{
|
|
29
|
+
kind: "resume";
|
|
30
|
+
coordinate: ResumeCoordinate;
|
|
31
|
+
}>;
|
|
32
|
+
}>): Promise<Session>;
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { abortable } from "../../abort.js";
|
|
3
|
+
import { AgentEventChannel } from "../../provider.js";
|
|
4
|
+
import { createEventState, mapEvent } from "./events.js";
|
|
5
|
+
import { coordinate, loadOpencode, OPENCODE_SDK_PROVIDER, parseModel } from "./session.js";
|
|
6
|
+
function object(value) {
|
|
7
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
8
|
+
? value : undefined;
|
|
9
|
+
}
|
|
10
|
+
function text(value) {
|
|
11
|
+
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
12
|
+
}
|
|
13
|
+
function diagnostic(error) {
|
|
14
|
+
const value = object(error);
|
|
15
|
+
const nested = object(value?.data);
|
|
16
|
+
return text(nested?.message) ?? text(value?.message) ?? String(error);
|
|
17
|
+
}
|
|
18
|
+
function opencodeSessionId(coordinateValue) {
|
|
19
|
+
if (!("sessionId" in coordinateValue) || coordinateValue.sessionId === undefined) {
|
|
20
|
+
throw new Error("OpenCode resume requires sessionId");
|
|
21
|
+
}
|
|
22
|
+
return coordinateValue.sessionId;
|
|
23
|
+
}
|
|
24
|
+
function admit(options) {
|
|
25
|
+
if (options.network !== undefined)
|
|
26
|
+
throw new Error("OpenCode does not support explicit network");
|
|
27
|
+
if (options.model !== undefined)
|
|
28
|
+
parseModel(options.model);
|
|
29
|
+
}
|
|
30
|
+
function eventValue(value) {
|
|
31
|
+
if (typeof value !== "string")
|
|
32
|
+
return value;
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(value);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return { type: value };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function promptBody(input, messageID) {
|
|
41
|
+
const model = input.options.model === undefined ? undefined : parseModel(input.options.model);
|
|
42
|
+
const promptText = [input.body, ...input.launchTells.map((tell) => tell.text)]
|
|
43
|
+
.filter((part) => part.length > 0)
|
|
44
|
+
.join("\n\n");
|
|
45
|
+
return {
|
|
46
|
+
messageID,
|
|
47
|
+
...(model === undefined ? {} : { model }),
|
|
48
|
+
...(input.options.effort === undefined ? {} : { variant: input.options.effort }),
|
|
49
|
+
...(input.options.systemPrompt === undefined ? {} : { system: input.options.systemPrompt }),
|
|
50
|
+
parts: [{ type: "text", text: promptText }],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function messageRows(value, sessionId) {
|
|
54
|
+
const response = object(value);
|
|
55
|
+
const rows = Array.isArray(response?.data) ? response.data : Array.isArray(value) ? value : [];
|
|
56
|
+
return rows.flatMap((row) => {
|
|
57
|
+
const entry = object(row);
|
|
58
|
+
const info = object(entry?.info);
|
|
59
|
+
if (info?.sessionID !== sessionId)
|
|
60
|
+
return [];
|
|
61
|
+
return [{ info, parts: Array.isArray(entry?.parts) ? entry.parts : [] }];
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function newest(rows) {
|
|
65
|
+
return rows.reduce((latest, row) => {
|
|
66
|
+
if (latest === undefined)
|
|
67
|
+
return row;
|
|
68
|
+
const at = Number(object(row.info.time)?.created ?? 0);
|
|
69
|
+
const latestAt = Number(object(latest.info.time)?.created ?? 0);
|
|
70
|
+
return at >= latestAt ? row : latest;
|
|
71
|
+
}, undefined);
|
|
72
|
+
}
|
|
73
|
+
async function readTurnResult(input) {
|
|
74
|
+
try {
|
|
75
|
+
const response = await input.session.messages({
|
|
76
|
+
path: { id: input.sessionId },
|
|
77
|
+
query: { directory: input.cwd },
|
|
78
|
+
throwOnError: true,
|
|
79
|
+
});
|
|
80
|
+
const rows = messageRows(response, input.sessionId);
|
|
81
|
+
const user = rows.find((row) => row.info.role === "user" && row.info.id === input.messageID);
|
|
82
|
+
if (user === undefined)
|
|
83
|
+
return undefined;
|
|
84
|
+
const userId = text(user?.info.id);
|
|
85
|
+
const assistant = newest(rows.filter((row) => row.info.role === "assistant" && row.info.parentID === userId));
|
|
86
|
+
const historyId = text(assistant?.info.id);
|
|
87
|
+
if (input.state.failure !== undefined)
|
|
88
|
+
return { kind: "failed", diagnostic: input.state.failure };
|
|
89
|
+
if (assistant === undefined)
|
|
90
|
+
return { kind: "failed", diagnostic: "OpenCode completed without a native assistant answer" };
|
|
91
|
+
if (assistant.info.error !== undefined)
|
|
92
|
+
return { kind: "failed", diagnostic: diagnostic(assistant.info.error) };
|
|
93
|
+
const answer = assistant.parts
|
|
94
|
+
.map((part) => object(part))
|
|
95
|
+
.filter((part) => part?.type === "text")
|
|
96
|
+
.map((part) => part?.text)
|
|
97
|
+
.filter((part) => typeof part === "string")
|
|
98
|
+
.join("\n\n");
|
|
99
|
+
return historyId === undefined ? { kind: "answered", answer } : { kind: "answered", answer, historyId };
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
return { kind: "failed", diagnostic: diagnostic(error) };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function nativeSessionId(value) {
|
|
106
|
+
const event = object(value);
|
|
107
|
+
const properties = object(event?.properties);
|
|
108
|
+
const direct = text(properties?.sessionID);
|
|
109
|
+
if (direct !== undefined)
|
|
110
|
+
return direct;
|
|
111
|
+
const info = object(properties?.info);
|
|
112
|
+
const part = object(properties?.part);
|
|
113
|
+
return text(info?.sessionID) ?? text(part?.sessionID);
|
|
114
|
+
}
|
|
115
|
+
function scopedProperties(value, sessionId) {
|
|
116
|
+
const properties = object(object(value)?.properties);
|
|
117
|
+
return nativeSessionId(value) === sessionId ? properties : undefined;
|
|
118
|
+
}
|
|
119
|
+
function stopIterator(iterator) {
|
|
120
|
+
const stopped = iterator?.return?.(undefined);
|
|
121
|
+
void stopped?.catch(() => undefined);
|
|
122
|
+
}
|
|
123
|
+
function nativeProgress(type, properties, busy) {
|
|
124
|
+
if (type === "session.idle")
|
|
125
|
+
return { busy, terminal: busy };
|
|
126
|
+
if (type !== "session.status")
|
|
127
|
+
return { busy, terminal: false };
|
|
128
|
+
const status = object(properties.status)?.type;
|
|
129
|
+
const nextBusy = busy || status === "busy";
|
|
130
|
+
return { busy: nextBusy, terminal: status === "idle" && nextBusy };
|
|
131
|
+
}
|
|
132
|
+
function startsTurn(value, sessionId, messageID) {
|
|
133
|
+
const event = object(value);
|
|
134
|
+
const info = object(object(event?.properties)?.info);
|
|
135
|
+
return event?.type === "message.updated"
|
|
136
|
+
&& info?.sessionID === sessionId
|
|
137
|
+
&& info.role === "user"
|
|
138
|
+
&& info.id === messageID;
|
|
139
|
+
}
|
|
140
|
+
async function observeTurn(input) {
|
|
141
|
+
let active = false;
|
|
142
|
+
let busy = false;
|
|
143
|
+
try {
|
|
144
|
+
for (;;) {
|
|
145
|
+
const next = await input.iterator.next();
|
|
146
|
+
if (next.done)
|
|
147
|
+
return { kind: "failed", diagnostic: "OpenCode event stream ended before Turn completion" };
|
|
148
|
+
const value = eventValue(next.value);
|
|
149
|
+
const observedSessionId = nativeSessionId(value);
|
|
150
|
+
if (observedSessionId !== undefined && observedSessionId !== input.sessionId)
|
|
151
|
+
continue;
|
|
152
|
+
const event = object(value);
|
|
153
|
+
const properties = scopedProperties(value, input.sessionId);
|
|
154
|
+
if (properties === undefined) {
|
|
155
|
+
if (active)
|
|
156
|
+
mapEvent(value, input.events, input.state);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (startsTurn(value, input.sessionId, input.messageID)) {
|
|
160
|
+
active = true;
|
|
161
|
+
busy = false;
|
|
162
|
+
}
|
|
163
|
+
const submittedError = event?.type === "session.error" && input.submissionState.started;
|
|
164
|
+
if (!active && !submittedError)
|
|
165
|
+
continue;
|
|
166
|
+
mapEvent(value, input.events, input.state);
|
|
167
|
+
if (event?.type === "session.error") {
|
|
168
|
+
return { kind: "failed", diagnostic: input.state.failure ?? diagnostic(properties.error) };
|
|
169
|
+
}
|
|
170
|
+
const progress = nativeProgress(event?.type, properties, busy);
|
|
171
|
+
busy = progress.busy;
|
|
172
|
+
if (!progress.terminal)
|
|
173
|
+
continue;
|
|
174
|
+
const result = await readTurnResult(input);
|
|
175
|
+
if (result !== undefined)
|
|
176
|
+
return result;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
return { kind: "failed", diagnostic: `OpenCode event stream failed: ${diagnostic(error)}` };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function terminalSettlement(events, close) {
|
|
184
|
+
let finish;
|
|
185
|
+
const completion = new Promise((resolve) => {
|
|
186
|
+
let settlement;
|
|
187
|
+
finish = (result) => {
|
|
188
|
+
settlement ??= (async () => {
|
|
189
|
+
events.end();
|
|
190
|
+
await close();
|
|
191
|
+
resolve(result);
|
|
192
|
+
})();
|
|
193
|
+
return settlement;
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
return { completion, finish };
|
|
197
|
+
}
|
|
198
|
+
async function drive(execution, input, loader) {
|
|
199
|
+
admit(input.options);
|
|
200
|
+
const signal = input.signal ?? new AbortController().signal;
|
|
201
|
+
const resumeSessionId = input.session.kind === "resume"
|
|
202
|
+
? opencodeSessionId(input.session.coordinate)
|
|
203
|
+
: undefined;
|
|
204
|
+
const abortController = new AbortController();
|
|
205
|
+
const abortSetup = () => abortController.abort(signal.reason);
|
|
206
|
+
signal.addEventListener("abort", abortSetup, { once: true });
|
|
207
|
+
signal.throwIfAborted();
|
|
208
|
+
const runtime = await abortable(loadOpencode(execution, input.cwd, abortController.signal, loader), abortController.signal, async (late) => await late.close());
|
|
209
|
+
let closing;
|
|
210
|
+
let iterator;
|
|
211
|
+
const closeOnce = () => {
|
|
212
|
+
closing ??= runtime.close();
|
|
213
|
+
return closing;
|
|
214
|
+
};
|
|
215
|
+
try {
|
|
216
|
+
const session = runtime.client.session;
|
|
217
|
+
const sessionResponse = await abortable(input.session.kind === "fresh"
|
|
218
|
+
? session.create({ query: { directory: input.cwd }, throwOnError: true })
|
|
219
|
+
: session.get({ path: { id: resumeSessionId }, query: { directory: input.cwd }, throwOnError: true }), abortController.signal);
|
|
220
|
+
const info = object(object(sessionResponse)?.data) ?? object(sessionResponse);
|
|
221
|
+
const sessionId = text(info?.id) ?? resumeSessionId;
|
|
222
|
+
if (sessionId === undefined)
|
|
223
|
+
throw new Error("OpenCode did not return a session id");
|
|
224
|
+
const events = new AgentEventChannel();
|
|
225
|
+
const state = createEventState(sessionId);
|
|
226
|
+
const messageID = `msg_${randomUUID().replaceAll("-", "")}`;
|
|
227
|
+
const submissionState = { started: false };
|
|
228
|
+
events.emit({ type: "session", coordinate: coordinate(sessionId) });
|
|
229
|
+
const streamResult = await abortable(runtime.client.event.subscribe({ query: { directory: input.cwd } }), abortController.signal, async (late) => { await late.stream[Symbol.asyncIterator]().return?.(undefined); });
|
|
230
|
+
iterator = streamResult.stream[Symbol.asyncIterator]();
|
|
231
|
+
const { completion, finish } = terminalSettlement(events, closeOnce);
|
|
232
|
+
const observation = observeTurn({
|
|
233
|
+
iterator, session, sessionId, cwd: input.cwd, messageID, events, state, submissionState,
|
|
234
|
+
});
|
|
235
|
+
submissionState.started = true;
|
|
236
|
+
await abortable(session.promptAsync({
|
|
237
|
+
path: { id: sessionId },
|
|
238
|
+
query: { directory: input.cwd },
|
|
239
|
+
body: promptBody(input, messageID),
|
|
240
|
+
throwOnError: true,
|
|
241
|
+
}), abortController.signal);
|
|
242
|
+
void observation.then(finish);
|
|
243
|
+
signal.removeEventListener("abort", abortSetup);
|
|
244
|
+
return {
|
|
245
|
+
admission: { fence: sessionId },
|
|
246
|
+
events,
|
|
247
|
+
completion,
|
|
248
|
+
abort: async () => {
|
|
249
|
+
abortController.abort();
|
|
250
|
+
void session.abort({ path: { id: sessionId }, query: { directory: input.cwd }, throwOnError: true }).catch(() => undefined);
|
|
251
|
+
await finish({ kind: "failed", diagnostic: "OpenCode session interrupted" });
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
signal.removeEventListener("abort", abortSetup);
|
|
257
|
+
stopIterator(iterator);
|
|
258
|
+
await closeOnce();
|
|
259
|
+
throw error;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
export function createOpencodeProvider(input = { name: OPENCODE_SDK_PROVIDER, kind: "opencode-sdk" }) {
|
|
263
|
+
const execution = "kind" in input ? input : { name: OPENCODE_SDK_PROVIDER, kind: "opencode-sdk" };
|
|
264
|
+
const loader = "loader" in input ? input.loader : undefined;
|
|
265
|
+
return {
|
|
266
|
+
confinement: ({ cwd }) => ({ kind: "declared", writableRoots: [cwd] }),
|
|
267
|
+
admitOptions(options) {
|
|
268
|
+
try {
|
|
269
|
+
admit(options);
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
return { kind: "refused", diagnostic: diagnostic(error) };
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
kind: "admitted",
|
|
276
|
+
options: Object.freeze({ ...options }),
|
|
277
|
+
...(options.readonly === undefined ? {} : {
|
|
278
|
+
readonly: {
|
|
279
|
+
enforcement: "none",
|
|
280
|
+
diagnostic: "OpenCode V1 cannot remove task-surface mutation capabilities",
|
|
281
|
+
},
|
|
282
|
+
}),
|
|
283
|
+
};
|
|
284
|
+
},
|
|
285
|
+
start: (input) => drive(execution, input, loader),
|
|
286
|
+
resume: (input) => drive(execution, input, loader),
|
|
287
|
+
fork: async (input) => {
|
|
288
|
+
const sessionId = opencodeSessionId(input.session);
|
|
289
|
+
const runtime = await loadOpencode(execution, input.cwd, new AbortController().signal, loader);
|
|
290
|
+
try {
|
|
291
|
+
const result = await runtime.client.session.fork({
|
|
292
|
+
path: { id: sessionId },
|
|
293
|
+
query: { directory: input.cwd },
|
|
294
|
+
body: { messageID: input.at },
|
|
295
|
+
});
|
|
296
|
+
const info = object(object(result)?.data) ?? object(result);
|
|
297
|
+
const id = text(info?.id);
|
|
298
|
+
if (id === undefined || id === sessionId)
|
|
299
|
+
throw new Error("OpenCode fork returned an invalid session id");
|
|
300
|
+
return { session: coordinate(id) };
|
|
301
|
+
}
|
|
302
|
+
finally {
|
|
303
|
+
await runtime.close();
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
export const opencodeProvider = createOpencodeProvider();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type OpencodeClient } from "@opencode-ai/sdk";
|
|
2
|
+
import type { ResumeCoordinate } from "../../heart/index.js";
|
|
3
|
+
import type { ProviderExecution } from "../../provider-recipe.js";
|
|
4
|
+
export type OpencodeSdkSession = Pick<OpencodeClient["session"], "create" | "get" | "fork" | "abort" | "promptAsync" | "messages">;
|
|
5
|
+
export type OpencodeSdkEvent = Pick<OpencodeClient["event"], "subscribe">;
|
|
6
|
+
export type OpencodeSdkLoader = (cwd: string) => Promise<Readonly<{
|
|
7
|
+
client: {
|
|
8
|
+
session: OpencodeSdkSession;
|
|
9
|
+
event: OpencodeSdkEvent;
|
|
10
|
+
};
|
|
11
|
+
close?: () => Promise<void> | void;
|
|
12
|
+
}>>;
|
|
13
|
+
export declare const OPENCODE_SDK_PROVIDER: "opencode-sdk";
|
|
14
|
+
export declare function parseModel(model: string): Readonly<{
|
|
15
|
+
providerID: string;
|
|
16
|
+
modelID: string;
|
|
17
|
+
}>;
|
|
18
|
+
export type OpencodeRuntime = Readonly<{
|
|
19
|
+
client: {
|
|
20
|
+
session: OpencodeSdkSession;
|
|
21
|
+
event: OpencodeSdkEvent;
|
|
22
|
+
};
|
|
23
|
+
close: () => Promise<void>;
|
|
24
|
+
}>;
|
|
25
|
+
export declare function loadOpencode(execution: ProviderExecution, cwd: string, signal: AbortSignal, loader?: OpencodeSdkLoader): Promise<OpencodeRuntime>;
|
|
26
|
+
export declare function coordinate(sessionId: string): ResumeCoordinate;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import net from "node:net";
|
|
2
|
+
import { abortable } from "../../abort.js";
|
|
3
|
+
import { spawnDetachedProcess } from "../../../runtime/proc/run.js";
|
|
4
|
+
import { createOpencodeClient } from "@opencode-ai/sdk";
|
|
5
|
+
export const OPENCODE_SDK_PROVIDER = "opencode-sdk";
|
|
6
|
+
export function parseModel(model) {
|
|
7
|
+
const slash = model.indexOf("/");
|
|
8
|
+
if (slash <= 0 || slash === model.length - 1)
|
|
9
|
+
throw new Error("OpenCode model must be <provider>/<model>");
|
|
10
|
+
return { providerID: model.slice(0, slash), modelID: model.slice(slash + 1) };
|
|
11
|
+
}
|
|
12
|
+
async function waitReady(client, cwd, signal) {
|
|
13
|
+
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
14
|
+
if (signal.aborted)
|
|
15
|
+
throw new Error("OpenCode startup aborted");
|
|
16
|
+
try {
|
|
17
|
+
await abortable(client.session.list({ query: { directory: cwd }, throwOnError: true }), signal);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw new Error("OpenCode server did not become ready within 10000ms");
|
|
25
|
+
}
|
|
26
|
+
async function availablePort() {
|
|
27
|
+
return await new Promise((resolve, reject) => {
|
|
28
|
+
const server = net.createServer();
|
|
29
|
+
server.once("error", reject);
|
|
30
|
+
server.listen(0, "127.0.0.1", () => {
|
|
31
|
+
const address = server.address();
|
|
32
|
+
if (address === null || typeof address === "string") {
|
|
33
|
+
server.close();
|
|
34
|
+
reject(new Error("OpenCode port unavailable"));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
server.close((error) => error === undefined ? resolve(address.port) : reject(error));
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export async function loadOpencode(execution, cwd, signal, loader) {
|
|
42
|
+
if (loader) {
|
|
43
|
+
const loaded = await loader(cwd);
|
|
44
|
+
return { client: loaded.client, close: async () => { await loaded.close?.(); } };
|
|
45
|
+
}
|
|
46
|
+
const port = await availablePort();
|
|
47
|
+
const owned = await spawnDetachedProcess({
|
|
48
|
+
argv: [execution.executable ?? "opencode", "serve", "--hostname", "127.0.0.1", "--port", String(port)],
|
|
49
|
+
cwd,
|
|
50
|
+
env: { ...process.env, ...execution.env },
|
|
51
|
+
log: `${cwd}/.opencode.log`,
|
|
52
|
+
});
|
|
53
|
+
const client = createOpencodeClient({ baseUrl: `http://127.0.0.1:${port}`, directory: cwd });
|
|
54
|
+
try {
|
|
55
|
+
await waitReady(client, cwd, signal);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
await owned.terminate();
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
return { client, close: async () => { await owned.terminate(); } };
|
|
62
|
+
}
|
|
63
|
+
export function coordinate(sessionId) { return { sessionId }; }
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { type AgentEvent, type ToolCall } from "../../provider.js";
|
|
3
|
+
export declare const PI_EVENT_DISPOSITIONS: {
|
|
4
|
+
readonly agent_end: "drop";
|
|
5
|
+
readonly agent_settled: "drop";
|
|
6
|
+
readonly agent_start: "drop";
|
|
7
|
+
readonly auto_retry_end: "drop";
|
|
8
|
+
readonly auto_retry_start: "note";
|
|
9
|
+
readonly compaction_end: "note";
|
|
10
|
+
readonly compaction_start: "drop";
|
|
11
|
+
readonly entry_appended: "drop";
|
|
12
|
+
readonly message_end: "message";
|
|
13
|
+
readonly message_start: "drop";
|
|
14
|
+
readonly message_update: "drop";
|
|
15
|
+
readonly queue_update: "drop";
|
|
16
|
+
readonly session_info_changed: "drop";
|
|
17
|
+
readonly thinking_level_changed: "drop";
|
|
18
|
+
readonly tool_execution_end: "tool-end";
|
|
19
|
+
readonly tool_execution_start: "tool-start";
|
|
20
|
+
readonly tool_execution_update: "drop";
|
|
21
|
+
readonly turn_end: "drop";
|
|
22
|
+
readonly turn_start: "drop";
|
|
23
|
+
};
|
|
24
|
+
export type PiEventState = {
|
|
25
|
+
answer: string;
|
|
26
|
+
assistantSeen: boolean;
|
|
27
|
+
tools: Map<string, Readonly<{
|
|
28
|
+
name: string;
|
|
29
|
+
call: ToolCall;
|
|
30
|
+
}>>;
|
|
31
|
+
};
|
|
32
|
+
export declare function translatePiEvent(event: AgentSessionEvent, state: PiEventState): readonly AgentEvent[];
|
|
33
|
+
export declare function piTerminalFailure(messages: readonly unknown[]): string | null;
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { noteEvent, unknownEvent } from "../../provider.js";
|
|
2
|
+
export const PI_EVENT_DISPOSITIONS = {
|
|
3
|
+
agent_end: "drop",
|
|
4
|
+
agent_settled: "drop",
|
|
5
|
+
agent_start: "drop",
|
|
6
|
+
auto_retry_end: "drop",
|
|
7
|
+
auto_retry_start: "note",
|
|
8
|
+
compaction_end: "note",
|
|
9
|
+
compaction_start: "drop",
|
|
10
|
+
entry_appended: "drop",
|
|
11
|
+
message_end: "message",
|
|
12
|
+
message_start: "drop",
|
|
13
|
+
message_update: "drop",
|
|
14
|
+
queue_update: "drop",
|
|
15
|
+
session_info_changed: "drop",
|
|
16
|
+
thinking_level_changed: "drop",
|
|
17
|
+
tool_execution_end: "tool-end",
|
|
18
|
+
tool_execution_start: "tool-start",
|
|
19
|
+
tool_execution_update: "drop",
|
|
20
|
+
turn_end: "drop",
|
|
21
|
+
turn_start: "drop",
|
|
22
|
+
};
|
|
23
|
+
function record(value) {
|
|
24
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
25
|
+
? value : null;
|
|
26
|
+
}
|
|
27
|
+
function textContent(content) {
|
|
28
|
+
if (typeof content === "string")
|
|
29
|
+
return content;
|
|
30
|
+
if (!Array.isArray(content))
|
|
31
|
+
return "";
|
|
32
|
+
return content.map((block) => {
|
|
33
|
+
const value = record(block);
|
|
34
|
+
return value?.type === "text" && typeof value.text === "string" ? value.text : "";
|
|
35
|
+
}).join("");
|
|
36
|
+
}
|
|
37
|
+
function hasTextContent(content) {
|
|
38
|
+
if (typeof content === "string")
|
|
39
|
+
return true;
|
|
40
|
+
return Array.isArray(content) && content.some((block) => {
|
|
41
|
+
const value = record(block);
|
|
42
|
+
return value?.type === "text" && typeof value.text === "string";
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function positiveLine(value) {
|
|
46
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 1 ? value : undefined;
|
|
47
|
+
}
|
|
48
|
+
function optionalText(value) {
|
|
49
|
+
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
|
50
|
+
}
|
|
51
|
+
function runCall(name, value) {
|
|
52
|
+
return name === "bash" && typeof value.command === "string"
|
|
53
|
+
? { kind: "run", command: value.command }
|
|
54
|
+
: undefined;
|
|
55
|
+
}
|
|
56
|
+
function readCall(name, value) {
|
|
57
|
+
if (name !== "read" || typeof value.path !== "string")
|
|
58
|
+
return undefined;
|
|
59
|
+
const offset = positiveLine(value.offset);
|
|
60
|
+
const limit = positiveLine(value.limit);
|
|
61
|
+
return {
|
|
62
|
+
kind: "read",
|
|
63
|
+
path: value.path,
|
|
64
|
+
...(offset === undefined ? {} : { offset }),
|
|
65
|
+
...(limit === undefined ? {} : { limit }),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function searchCall(name, value) {
|
|
69
|
+
if (name !== "grep" && name !== "find")
|
|
70
|
+
return undefined;
|
|
71
|
+
const query = value.pattern ?? value.query;
|
|
72
|
+
if (typeof query !== "string")
|
|
73
|
+
return undefined;
|
|
74
|
+
const path = optionalText(value.path);
|
|
75
|
+
const glob = optionalText(value.glob);
|
|
76
|
+
return {
|
|
77
|
+
kind: "search",
|
|
78
|
+
query,
|
|
79
|
+
scope: name === "find" ? "files" : "content",
|
|
80
|
+
...(path === undefined ? {} : { path }),
|
|
81
|
+
...(name === "grep" && glob !== undefined ? { glob } : {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function fileChangeCall(name, value) {
|
|
85
|
+
if ((name !== "edit" && name !== "write") || typeof value.path !== "string")
|
|
86
|
+
return undefined;
|
|
87
|
+
return { kind: "fileChange", changes: [{ op: name === "write" ? "add" : "update", path: value.path }] };
|
|
88
|
+
}
|
|
89
|
+
function toolCall(name, args) {
|
|
90
|
+
const value = record(args) ?? {};
|
|
91
|
+
return runCall(name, value)
|
|
92
|
+
?? readCall(name, value)
|
|
93
|
+
?? searchCall(name, value)
|
|
94
|
+
?? fileChangeCall(name, value)
|
|
95
|
+
?? { kind: "other", display: name };
|
|
96
|
+
}
|
|
97
|
+
function translateNote(event) {
|
|
98
|
+
if (event.type === "auto_retry_start")
|
|
99
|
+
return [noteEvent(`Retrying request ${event.attempt}/${event.maxAttempts}: ${event.errorMessage}`)];
|
|
100
|
+
return event.errorMessage === undefined ? [] : [noteEvent(event.errorMessage)];
|
|
101
|
+
}
|
|
102
|
+
function translateToolStart(event, state) {
|
|
103
|
+
const call = toolCall(event.toolName, event.args);
|
|
104
|
+
state.tools.set(event.toolCallId, { name: event.toolName, call });
|
|
105
|
+
return [{ type: "tool", phase: "started", id: event.toolCallId, name: event.toolName, call }];
|
|
106
|
+
}
|
|
107
|
+
function translateToolEnd(event, state) {
|
|
108
|
+
const started = state.tools.get(event.toolCallId);
|
|
109
|
+
state.tools.delete(event.toolCallId);
|
|
110
|
+
const name = started?.name ?? event.toolName;
|
|
111
|
+
return [{
|
|
112
|
+
type: "tool", phase: "completed", id: event.toolCallId, name,
|
|
113
|
+
call: started?.call ?? { kind: "other", display: name },
|
|
114
|
+
result: { status: event.isError ? "error" : "ok" },
|
|
115
|
+
}];
|
|
116
|
+
}
|
|
117
|
+
function translateMessage(event, state) {
|
|
118
|
+
const message = record(event.message);
|
|
119
|
+
if (message?.role !== "assistant")
|
|
120
|
+
return [];
|
|
121
|
+
state.assistantSeen = true;
|
|
122
|
+
const translated = [];
|
|
123
|
+
if (Array.isArray(message.content)) {
|
|
124
|
+
for (const block of message.content) {
|
|
125
|
+
const value = record(block);
|
|
126
|
+
if (value?.type === "thinking" &&
|
|
127
|
+
value.redacted !== true &&
|
|
128
|
+
typeof value.thinking === "string" &&
|
|
129
|
+
value.thinking.trim().length > 0) {
|
|
130
|
+
translated.push({ type: "thought", text: value.thinking });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const text = textContent(message.content);
|
|
135
|
+
state.answer = text;
|
|
136
|
+
if (hasTextContent(message.content)) {
|
|
137
|
+
translated.push({ type: "assistant", text });
|
|
138
|
+
}
|
|
139
|
+
return translated;
|
|
140
|
+
}
|
|
141
|
+
export function translatePiEvent(event, state) {
|
|
142
|
+
const disposition = PI_EVENT_DISPOSITIONS[event.type];
|
|
143
|
+
if (disposition === "drop")
|
|
144
|
+
return [];
|
|
145
|
+
if (disposition === "note")
|
|
146
|
+
return translateNote(event);
|
|
147
|
+
if (disposition === "tool-start")
|
|
148
|
+
return translateToolStart(event, state);
|
|
149
|
+
if (disposition === "tool-end")
|
|
150
|
+
return translateToolEnd(event, state);
|
|
151
|
+
if (disposition === "message")
|
|
152
|
+
return translateMessage(event, state);
|
|
153
|
+
return [unknownEvent(event.type)];
|
|
154
|
+
}
|
|
155
|
+
export function piTerminalFailure(messages) {
|
|
156
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
157
|
+
const message = record(messages[index]);
|
|
158
|
+
if (message?.role !== "assistant")
|
|
159
|
+
continue;
|
|
160
|
+
if (message.stopReason !== "error")
|
|
161
|
+
return null;
|
|
162
|
+
return typeof message.errorMessage === "string" && message.errorMessage.trim().length > 0
|
|
163
|
+
? message.errorMessage : "Pi provider ended with an error";
|
|
164
|
+
}
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createAgentSession, DefaultResourceLoader, getAgentDir, ModelRuntime, SessionManager, type CreateAgentSessionOptions } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { ProviderExecution } from "../../provider-recipe.js";
|
|
3
|
+
import { type ProviderAdapter } from "../../provider.js";
|
|
4
|
+
export type PiSdk = Readonly<{
|
|
5
|
+
createAgentSession(options?: CreateAgentSessionOptions): ReturnType<typeof createAgentSession>;
|
|
6
|
+
DefaultResourceLoader: typeof DefaultResourceLoader;
|
|
7
|
+
getAgentDir: typeof getAgentDir;
|
|
8
|
+
ModelRuntime: typeof ModelRuntime;
|
|
9
|
+
SessionManager: typeof SessionManager;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function createPiProvider(execution?: ProviderExecution, load?: () => Promise<PiSdk>): ProviderAdapter;
|