@astrosheep/keiyaku 4.6.8 → 4.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -14
- package/build/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/build/plugins/claude/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/claude/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/claude/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/claude/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/claude/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/claude/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/claude/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/build/plugins/codex/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/codex/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/codex/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/codex/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/codex/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/codex/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/opencode/index.js +1 -1
- package/build/plugins/opencode/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/opencode/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/opencode/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/opencode/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/opencode/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/opencode/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/skills/keiyaku/SKILL.md +5 -6
- package/build/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/src/akuma/akuma-instance.d.ts +2 -0
- package/build/src/akuma/akuma-instance.js +10 -0
- package/build/src/akuma/akuma-observe.d.ts +33 -8
- package/build/src/akuma/akuma-observe.js +88 -64
- package/build/src/akuma/akuma-owner.d.ts +11 -2
- package/build/src/akuma/akuma-owner.js +45 -35
- package/build/src/akuma/akuma-selection-execution.js +9 -1
- package/build/src/akuma/akuma-stream.d.ts +22 -0
- package/build/src/akuma/akuma-stream.js +116 -0
- package/build/src/akuma/akuma-wait.d.ts +1 -40
- package/build/src/akuma/akuma-wait.js +23 -84
- package/build/src/akuma/akuma.d.ts +17 -11
- package/build/src/akuma/akuma.js +2 -0
- package/build/src/akuma/allowed.d.ts +7 -12
- package/build/src/akuma/allowed.js +13 -4
- package/build/src/akuma/body-supervisor.d.ts +5 -5
- package/build/src/akuma/body-supervisor.js +22 -20
- package/build/src/akuma/body.js +28 -1
- package/build/src/akuma/call-request.d.ts +12 -22
- package/build/src/akuma/heart/activity-schema.d.ts +12 -0
- package/build/src/akuma/heart/activity-schema.js +6 -0
- package/build/src/akuma/heart/index.d.ts +59 -2
- package/build/src/akuma/heart/index.js +87 -8
- package/build/src/akuma/heart/rows.js +5 -3
- package/build/src/akuma/heart/soul.d.ts +9 -0
- package/build/src/akuma/heart/soul.js +29 -0
- package/build/src/akuma/heart/storage.d.ts +15 -0
- package/build/src/akuma/heart/storage.js +55 -0
- package/build/src/akuma/heart/timeline.d.ts +7 -0
- package/build/src/akuma/heart/timeline.js +10 -0
- package/build/src/akuma/index.d.ts +1 -0
- package/build/src/akuma/projection.d.ts +25 -0
- package/build/src/akuma/projection.js +24 -7
- package/build/src/akuma/provider-recipe.d.ts +11 -0
- package/build/src/akuma/provider-recipe.js +23 -0
- package/build/src/akuma/provider.d.ts +25 -0
- package/build/src/akuma/provider.js +99 -7
- package/build/src/akuma/providers/acp/events.js +25 -2
- package/build/src/akuma/providers/claude/events.js +76 -10
- package/build/src/akuma/providers/claude/index.js +2 -5
- package/build/src/akuma/providers/codex-app-server/events.js +17 -2
- package/build/src/akuma/providers/codex-app-server/index.js +15 -9
- package/build/src/akuma/providers/decode.d.ts +8 -0
- package/build/src/akuma/providers/decode.js +21 -0
- package/build/src/akuma/providers/grok-build/index.js +10 -26
- package/build/src/akuma/providers/opencode-sdk/event-stream.d.ts +3 -3
- package/build/src/akuma/providers/opencode-sdk/event-stream.js +2 -25
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +7 -43
- package/build/src/akuma/providers/opencode-sdk/events.js +147 -284
- package/build/src/akuma/providers/opencode-sdk/index.js +267 -421
- package/build/src/akuma/providers/opencode-sdk/native-http.d.ts +9 -0
- package/build/src/akuma/providers/opencode-sdk/native-http.js +69 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +42 -36
- package/build/src/akuma/providers/opencode-sdk/session.js +75 -82
- package/build/src/akuma/providers/paseo/events.d.ts +8 -0
- package/build/src/akuma/providers/paseo/events.js +58 -11
- package/build/src/akuma/providers/paseo/index.js +11 -7
- package/build/src/akuma/providers/pi/events.d.ts +2 -0
- package/build/src/akuma/providers/pi/events.js +32 -24
- package/build/src/akuma/providers/pi/index.js +21 -35
- package/build/src/akuma/request-lifecycle.d.ts +1 -2
- package/build/src/akuma/request-lifecycle.js +2 -8
- package/build/src/akuma/request-rendezvous.js +38 -55
- package/build/src/akuma/request-serve.d.ts +1 -1
- package/build/src/akuma/request-serve.js +77 -113
- package/build/src/akuma/selection-observation.d.ts +57 -51
- package/build/src/akuma/selection-observation.js +3 -3
- package/build/src/akuma/turn-drive.d.ts +3 -0
- package/build/src/akuma/turn-drive.js +26 -5
- package/build/src/akuma-body.js +12 -22
- package/build/src/cli/akuma-caller.js +10 -0
- package/build/src/cli/commands/akuma-grammar.js +3 -4
- package/build/src/cli/commands/akuma.js +13 -51
- package/build/src/cli/commands/contract-grammar.d.ts +7 -6
- package/build/src/cli/commands/contract-grammar.js +27 -32
- package/build/src/cli/commands/contract-help.d.ts +16 -7
- package/build/src/cli/commands/contract-help.js +20 -11
- package/build/src/cli/commands/contract.js +11 -11
- package/build/src/cli/commands/debt-grammar.d.ts +19 -0
- package/build/src/cli/commands/debt-grammar.js +323 -0
- package/build/src/cli/commands/debt-query.d.ts +5 -0
- package/build/src/cli/commands/{task-query.js → debt-query.js} +9 -9
- package/build/src/cli/commands/{task.d.ts → debt.d.ts} +2 -2
- package/build/src/cli/commands/{task.js → debt.js} +83 -143
- package/build/src/cli/coordinates.d.ts +1 -1
- package/build/src/cli/coordinates.js +1 -1
- package/build/src/cli/parse.d.ts +4 -4
- package/build/src/cli/parse.js +17 -16
- package/build/src/cli/render/akuma-activity.d.ts +11 -83
- package/build/src/cli/render/akuma-activity.js +20 -382
- package/build/src/cli/render/catalog.d.ts +2 -2
- package/build/src/cli/render/catalog.js +5 -5
- package/build/src/cli/render/contract.js +2 -2
- package/build/src/cli/render/debt.d.ts +28 -0
- package/build/src/cli/render/{task.js → debt.js} +51 -112
- package/build/src/cli/render/kanshi.js +28 -28
- package/build/src/cli/render/marks.d.ts +5 -5
- package/build/src/cli/render/marks.js +4 -10
- package/build/src/cli/render/nuke.js +1 -1
- package/build/src/cli/runtime.js +3 -3
- package/build/src/cli/scan.d.ts +1 -1
- package/build/src/cli/usage.d.ts +1 -1
- package/build/src/cli/usage.js +4 -4
- package/build/src/contract-worktree.js +3 -3
- package/build/src/debt/board.d.ts +160 -0
- package/build/src/{task → debt}/board.js +89 -89
- package/build/src/debt/catalog.d.ts +11 -0
- package/build/src/debt/catalog.js +59 -0
- package/build/src/{task → debt}/context.d.ts +1 -1
- package/build/src/{task → debt}/context.js +1 -1
- package/build/src/{task → debt}/created-observation.d.ts +6 -10
- package/build/src/{task → debt}/created-observation.js +7 -7
- package/build/src/debt/document.d.ts +37 -0
- package/build/src/{task → debt}/document.js +37 -37
- package/build/src/debt/identity.d.ts +21 -0
- package/build/src/{task → debt}/identity.js +35 -35
- package/build/src/debt/index.d.ts +111 -0
- package/build/src/debt/index.js +203 -0
- package/build/src/{task → debt}/input.d.ts +8 -8
- package/build/src/{task → debt}/input.js +21 -21
- package/build/src/debt/lifecycle-operations.d.ts +6 -0
- package/build/src/{task → debt}/lifecycle-operations.js +27 -31
- package/build/src/debt/mutation-result.d.ts +981 -0
- package/build/src/debt/mutation-result.js +109 -0
- package/build/src/debt/mutation.d.ts +424 -0
- package/build/src/debt/mutation.js +223 -0
- package/build/src/debt/operation-types.d.ts +71 -0
- package/build/src/debt/operations.d.ts +28 -0
- package/build/src/{task → debt}/operations.js +59 -60
- package/build/src/debt/query.d.ts +76 -0
- package/build/src/{task → debt}/query.js +55 -55
- package/build/src/{task → debt}/store.d.ts +13 -13
- package/build/src/{task → debt}/store.js +58 -49
- package/build/src/debt/view.d.ts +4 -0
- package/build/src/debt/view.js +7 -0
- package/build/src/identity/coordinates.d.ts +1 -1
- package/build/src/index.d.ts +13 -13
- package/build/src/index.js +9 -9
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/read.js +33 -33
- package/build/src/kanshi/report.d.ts +7 -7
- package/build/src/kanshi/select.js +4 -4
- package/build/src/library/akumas.d.ts +2 -3
- package/build/src/library/bind.d.ts +3 -3
- package/build/src/library/bind.js +5 -5
- package/build/src/library/contract-creation.js +5 -5
- package/build/src/library/contract-execution.js +4 -4
- package/build/src/library/contract-types.d.ts +2 -2
- package/build/src/library/input.d.ts +2 -2
- package/build/src/library/input.js +5 -5
- package/build/src/library/invocation.js +2 -2
- package/build/src/library/nuke.js +3 -3
- package/build/src/library/outcome.d.ts +126 -126
- package/build/src/library/outcome.js +3 -3
- package/build/src/library/selection.d.ts +1 -4
- package/build/src/library/selection.js +11 -62
- package/build/src/plugin/public.d.ts +5 -0
- package/build/src/plugin/runtime.js +3 -1
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settlement/fence.d.ts +3 -3
- package/build/src/settlement/fence.js +4 -4
- package/build/src/settlement/holder.d.ts +16 -16
- package/build/src/settlement/holder.js +69 -63
- package/build/src/settlement/settle.d.ts +5 -5
- package/build/src/settlement/settle.js +53 -53
- package/package.json +12 -6
- package/plugins/paseo/plugin/index.server.ts +17 -0
- package/plugins/paseo/plugin/paseo-plugin.json +4 -0
- package/plugins/paseo/plugin/server/attach.ts +52 -0
- package/plugins/paseo/plugin/server/keiyaku.ts +44 -0
- package/plugins/paseo/plugin/server/message.ts +60 -0
- package/plugins/paseo/plugin/server/mirror.ts +193 -0
- package/plugins/paseo/plugin/server/model.ts +14 -0
- package/plugins/paseo/plugin/server/provider.ts +276 -0
- package/plugins/paseo/plugin/server/sdk-worker.ts +237 -0
- package/plugins/paseo/plugin/server/sdk.ts +289 -0
- package/plugins/paseo/plugin/server/stop.ts +26 -0
- package/plugins/paseo/plugin/server/transport.ts +35 -0
- package/plugins/paseo/plugin/shared/attach-rpc.ts +10 -0
- package/plugins/paseo/plugin/shared/refresh-rpc.ts +6 -0
- package/plugins/paseo/plugin/tsconfig.json +6 -0
- package/skills/keiyaku/SKILL.md +5 -6
- package/skills/keiyaku-akuma/SKILL.md +1 -1
- package/skills/keiyaku-akuma/references/automation.md +8 -8
- package/skills/keiyaku-babysit/SKILL.md +1 -1
- package/skills/keiyaku-bind/SKILL.md +9 -9
- package/skills/keiyaku-debt/SKILL.md +91 -0
- package/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/claude/skills/keiyaku-task/SKILL.md +0 -104
- package/build/plugins/codex/skills/keiyaku-task/SKILL.md +0 -104
- package/build/plugins/opencode/skills/keiyaku-task/SKILL.md +0 -104
- package/build/skills/keiyaku-task/SKILL.md +0 -104
- package/build/src/cli/commands/task-grammar.d.ts +0 -19
- package/build/src/cli/commands/task-grammar.js +0 -384
- package/build/src/cli/commands/task-query.d.ts +0 -5
- package/build/src/cli/render/task.d.ts +0 -39
- package/build/src/task/board.d.ts +0 -168
- package/build/src/task/catalog.d.ts +0 -11
- package/build/src/task/catalog.js +0 -59
- package/build/src/task/compose-language.d.ts +0 -57
- package/build/src/task/compose-language.js +0 -364
- package/build/src/task/compose-parser.d.ts +0 -32
- package/build/src/task/compose-parser.js +0 -180
- package/build/src/task/compose.d.ts +0 -16
- package/build/src/task/compose.js +0 -189
- package/build/src/task/document.d.ts +0 -39
- package/build/src/task/identity.d.ts +0 -21
- package/build/src/task/index.d.ts +0 -138
- package/build/src/task/index.js +0 -269
- package/build/src/task/lifecycle-operations.d.ts +0 -6
- package/build/src/task/mutation-result.d.ts +0 -1726
- package/build/src/task/mutation-result.js +0 -198
- package/build/src/task/mutation.d.ts +0 -590
- package/build/src/task/mutation.js +0 -262
- package/build/src/task/operation-types.d.ts +0 -71
- package/build/src/task/operations.d.ts +0 -28
- package/build/src/task/query.d.ts +0 -76
- package/build/src/task/view.d.ts +0 -4
- package/build/src/task/view.js +0 -7
- package/skills/keiyaku-task/SKILL.md +0 -104
- /package/build/src/{task → debt}/operation-types.js +0 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type OpenCodeClient } from "@opencode/client";
|
|
2
|
+
/** The matching native SDK owns the dialect; this attempt owns its HTTP connections. */
|
|
3
|
+
export declare function createNativeClient(baseUrl: string, password: string): Readonly<{
|
|
4
|
+
sdk: OpenCodeClient;
|
|
5
|
+
subscribe(signal: AbortSignal): Promise<Readonly<{
|
|
6
|
+
stream: AsyncIterable<unknown>;
|
|
7
|
+
}>>;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { OpenCode } from "@opencode/client";
|
|
2
|
+
import { Agent } from "undici";
|
|
3
|
+
import { object } from "../decode.js";
|
|
4
|
+
/** The matching native SDK owns the dialect; this attempt owns its HTTP connections. */
|
|
5
|
+
export function createNativeClient(baseUrl, password) {
|
|
6
|
+
const dispatcher = new Agent();
|
|
7
|
+
const controller = new AbortController();
|
|
8
|
+
// Node's fetch accepts an Undici dispatcher in addition to standard RequestInit.
|
|
9
|
+
// Keep that native extension at this one SDK seam.
|
|
10
|
+
const ownedFetch = (input, init) => {
|
|
11
|
+
controller.signal.throwIfAborted();
|
|
12
|
+
const options = {
|
|
13
|
+
...init,
|
|
14
|
+
dispatcher,
|
|
15
|
+
signal: init?.signal == null ? controller.signal : AbortSignal.any([controller.signal, init.signal]),
|
|
16
|
+
};
|
|
17
|
+
return globalThis.fetch(input, options);
|
|
18
|
+
};
|
|
19
|
+
const sdk = OpenCode.make({
|
|
20
|
+
baseUrl,
|
|
21
|
+
fetch: ownedFetch,
|
|
22
|
+
headers: { authorization: `Basic ${Buffer.from(`opencode:${password}`).toString("base64")}` },
|
|
23
|
+
});
|
|
24
|
+
const subscribe = async (signal) => {
|
|
25
|
+
const iterator = sdk.event.subscribe({ signal })[Symbol.asyncIterator]();
|
|
26
|
+
try {
|
|
27
|
+
// The producer subscribes before emitting this frame. Headers alone are
|
|
28
|
+
// not a barrier against losing the opening native admission broadcast.
|
|
29
|
+
const connected = await iterator.next();
|
|
30
|
+
if (connected.done || object(connected.value)?.type !== "server.connected") {
|
|
31
|
+
throw new Error("OpenCode event feed did not confirm subscription");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
await iterator.return?.();
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
stream: {
|
|
40
|
+
[Symbol.asyncIterator]: () => ({
|
|
41
|
+
next: async () => {
|
|
42
|
+
const next = await iterator.next();
|
|
43
|
+
// Effect stream-failure frames are not native events. The generated
|
|
44
|
+
// SDK parser can yield their payload; never drop that into narration.
|
|
45
|
+
if (!next.done && typeof object(next.value)?.type !== "string") {
|
|
46
|
+
throw new Error("OpenCode native event feed failed");
|
|
47
|
+
}
|
|
48
|
+
return next;
|
|
49
|
+
},
|
|
50
|
+
return: () => iterator.return?.() ?? Promise.resolve({ done: true, value: undefined }),
|
|
51
|
+
}),
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
let closing;
|
|
56
|
+
return {
|
|
57
|
+
sdk,
|
|
58
|
+
subscribe,
|
|
59
|
+
close: () => {
|
|
60
|
+
closing ??= (async () => {
|
|
61
|
+
controller.abort();
|
|
62
|
+
// SDK iterator retirement is logical. Dispatcher destruction separately
|
|
63
|
+
// awaits closure of every socket, including requests aborted during setup.
|
|
64
|
+
await dispatcher.destroy();
|
|
65
|
+
})();
|
|
66
|
+
return closing;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -1,24 +1,49 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { SessionCreateInput, SessionSwitchModelInput } from "@opencode/client";
|
|
2
2
|
import type { ResumeCoordinate } from "../../heart/index.js";
|
|
3
3
|
import type { ProviderExecution } from "../../provider-recipe.js";
|
|
4
|
-
export type OpencodeSdkSession =
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export type OpencodeSdkSession = Readonly<{
|
|
5
|
+
create(input: {
|
|
6
|
+
body: SessionCreateInput;
|
|
7
|
+
}): Promise<unknown>;
|
|
8
|
+
get(input: {
|
|
9
|
+
id: string;
|
|
10
|
+
}): Promise<unknown>;
|
|
11
|
+
switchModel(input: {
|
|
12
|
+
id: string;
|
|
13
|
+
model: SessionSwitchModelInput["model"];
|
|
14
|
+
}): Promise<unknown>;
|
|
15
|
+
instructions(input: {
|
|
16
|
+
id: string;
|
|
17
|
+
text: string;
|
|
18
|
+
}): Promise<unknown>;
|
|
19
|
+
active(): Promise<unknown>;
|
|
20
|
+
fork(input: {
|
|
21
|
+
id: string;
|
|
22
|
+
before: string;
|
|
23
|
+
}): Promise<unknown>;
|
|
24
|
+
interrupt(input: {
|
|
25
|
+
id: string;
|
|
26
|
+
}): Promise<unknown>;
|
|
27
|
+
prompt(input: {
|
|
28
|
+
id: string;
|
|
29
|
+
body: {
|
|
30
|
+
id: string;
|
|
31
|
+
text: string;
|
|
32
|
+
};
|
|
33
|
+
}): Promise<unknown>;
|
|
34
|
+
message(input: {
|
|
35
|
+
id: string;
|
|
36
|
+
messageID: string;
|
|
37
|
+
}): Promise<unknown>;
|
|
38
|
+
}>;
|
|
10
39
|
export type OpencodeEventSubscriptionRequest = Readonly<{
|
|
11
40
|
directory: string;
|
|
12
|
-
/** Cancels the subscription's fetch, reader, and retry waiting. */
|
|
13
41
|
signal: AbortSignal;
|
|
14
|
-
/** Resolves without rejecting when `signal` aborts, and always clears its timer. */
|
|
15
|
-
sleep(milliseconds: number): Promise<void>;
|
|
16
|
-
}>;
|
|
17
|
-
type OpencodeEventSubscription = Readonly<{
|
|
18
|
-
stream: AsyncIterable<unknown>;
|
|
19
42
|
}>;
|
|
20
43
|
export type OpencodeSdkEvent = Readonly<{
|
|
21
|
-
subscribe(request: OpencodeEventSubscriptionRequest): Promise<
|
|
44
|
+
subscribe(request: OpencodeEventSubscriptionRequest): Promise<Readonly<{
|
|
45
|
+
stream: AsyncIterable<unknown>;
|
|
46
|
+
}>>;
|
|
22
47
|
}>;
|
|
23
48
|
export type OpencodeSdkLoader = (cwd: string, execution: ProviderExecution, signal: AbortSignal) => Promise<Readonly<{
|
|
24
49
|
client: {
|
|
@@ -29,31 +54,11 @@ export type OpencodeSdkLoader = (cwd: string, execution: ProviderExecution, sign
|
|
|
29
54
|
ready?: Promise<void>;
|
|
30
55
|
}>>;
|
|
31
56
|
export declare const OPENCODE_SDK_PROVIDER: "opencode-sdk";
|
|
32
|
-
/**
|
|
33
|
-
* Adapt the installed client's generated event endpoint to the adapter's
|
|
34
|
-
* subscription port.
|
|
35
|
-
*
|
|
36
|
-
* The generated per-endpoint `Options` type omits the SDK's runtime
|
|
37
|
-
* `sseSleepFn` option even though the client forwards every per-request key
|
|
38
|
-
* into `createSseClient`, so a caller cannot express a cancellable backoff
|
|
39
|
-
* through the published type. Declaring the port here keeps that one typing gap
|
|
40
|
-
* and its single adaptation at the SDK seam: no other module repeats a cast,
|
|
41
|
-
* the SDK's own parser and retry policy stay untouched, and an injected loader
|
|
42
|
-
* supplies the same port shape.
|
|
43
|
-
*/
|
|
44
|
-
export declare function opencodeEventPort(event: OpencodeClient["event"]): OpencodeSdkEvent;
|
|
45
57
|
export declare function messageId(sequence?: number): string;
|
|
46
58
|
export declare function parseModel(model: string): Readonly<{
|
|
47
59
|
providerID: string;
|
|
48
|
-
|
|
60
|
+
id: string;
|
|
49
61
|
}>;
|
|
50
|
-
/**
|
|
51
|
-
* Compose the config handed to a headless opencode server. The permission base
|
|
52
|
-
* layer fills only the classes the caller leaves unspecified: an explicit caller
|
|
53
|
-
* permission key wins per key and every non-permission key passes through
|
|
54
|
-
* untouched. A caller that supplies a non-object `permission` shorthand states
|
|
55
|
-
* the whole policy and keeps it.
|
|
56
|
-
*/
|
|
57
62
|
export declare function composeHeadlessPermissionConfig(config?: Readonly<Record<string, unknown>>): Record<string, unknown>;
|
|
58
63
|
export type OpencodeRuntime = Readonly<{
|
|
59
64
|
client: {
|
|
@@ -62,6 +67,8 @@ export type OpencodeRuntime = Readonly<{
|
|
|
62
67
|
};
|
|
63
68
|
close: () => Promise<void>;
|
|
64
69
|
}>;
|
|
70
|
+
/** A successful HTML fallback, or a JSON response from another API, is not readiness. */
|
|
71
|
+
export declare function waitReady(json: (path: string) => Promise<unknown>, signal: AbortSignal): Promise<void>;
|
|
65
72
|
export declare function loadOpencode(input: Readonly<{
|
|
66
73
|
execution: ProviderExecution;
|
|
67
74
|
cwd: string;
|
|
@@ -71,4 +78,3 @@ export declare function loadOpencode(input: Readonly<{
|
|
|
71
78
|
requests?: string;
|
|
72
79
|
}>): Promise<OpencodeRuntime>;
|
|
73
80
|
export declare function coordinate(sessionId: string): ResumeCoordinate;
|
|
74
|
-
export {};
|
|
@@ -3,90 +3,59 @@ import { randomBytes } from "node:crypto";
|
|
|
3
3
|
import { abortable } from "../../abort.js";
|
|
4
4
|
import { akumaExecutionEnvironment } from "../execution-environment.js";
|
|
5
5
|
import { spawnDetachedProcess } from "../../../runtime/proc/run.js";
|
|
6
|
-
import {
|
|
6
|
+
import { object } from "../decode.js";
|
|
7
|
+
import { createNativeClient } from "./native-http.js";
|
|
7
8
|
export const OPENCODE_SDK_PROVIDER = "opencode-sdk";
|
|
8
|
-
/**
|
|
9
|
-
* Adapt the installed client's generated event endpoint to the adapter's
|
|
10
|
-
* subscription port.
|
|
11
|
-
*
|
|
12
|
-
* The generated per-endpoint `Options` type omits the SDK's runtime
|
|
13
|
-
* `sseSleepFn` option even though the client forwards every per-request key
|
|
14
|
-
* into `createSseClient`, so a caller cannot express a cancellable backoff
|
|
15
|
-
* through the published type. Declaring the port here keeps that one typing gap
|
|
16
|
-
* and its single adaptation at the SDK seam: no other module repeats a cast,
|
|
17
|
-
* the SDK's own parser and retry policy stay untouched, and an injected loader
|
|
18
|
-
* supplies the same port shape.
|
|
19
|
-
*/
|
|
20
|
-
export function opencodeEventPort(event) {
|
|
21
|
-
return {
|
|
22
|
-
subscribe: (request) => {
|
|
23
|
-
const adapted = {
|
|
24
|
-
query: { directory: request.directory },
|
|
25
|
-
signal: request.signal,
|
|
26
|
-
};
|
|
27
|
-
// Runtime-only key omitted from the generated Options type; the installed
|
|
28
|
-
// client forwards it to createSseClient unchanged.
|
|
29
|
-
adapted.sseSleepFn = request.sleep;
|
|
30
|
-
return event.subscribe(adapted);
|
|
31
|
-
},
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
9
|
export function messageId(sequence = 0) {
|
|
35
|
-
const timestamp = BigInt(
|
|
36
|
-
|
|
37
|
-
bytes.writeUIntBE(Number(timestamp & 0xffffffffffffn), 0, 6);
|
|
38
|
-
const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
|
39
|
-
const suffix = [...randomBytes(14)].map((byte) => alphabet[byte % alphabet.length]).join("");
|
|
40
|
-
return `msg_${bytes.toString("hex")}${suffix}`;
|
|
10
|
+
const timestamp = BigInt(Date.now()) * 0x1000n + BigInt(sequence);
|
|
11
|
+
return `msg_${timestamp.toString(16)}${randomBytes(14).toString("hex")}`;
|
|
41
12
|
}
|
|
42
13
|
export function parseModel(model) {
|
|
43
14
|
const slash = model.indexOf("/");
|
|
44
15
|
if (slash <= 0 || slash === model.length - 1)
|
|
45
16
|
throw new Error("OpenCode model must be <provider>/<model>");
|
|
46
|
-
return { providerID: model.slice(0, slash),
|
|
17
|
+
return { providerID: model.slice(0, slash), id: model.slice(slash + 1) };
|
|
47
18
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
*
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
* allowed (the question tool awaits an answer by semantics). Every other class
|
|
56
|
-
* matches the allow-all rule and cannot await.
|
|
57
|
-
*/
|
|
58
|
-
const HEADLESS_PERMISSION_BASE = Object.freeze({
|
|
59
|
-
external_directory: "allow",
|
|
60
|
-
doom_loop: "allow",
|
|
61
|
-
read: "allow",
|
|
62
|
-
question: "deny",
|
|
63
|
-
});
|
|
64
|
-
/**
|
|
65
|
-
* Compose the config handed to a headless opencode server. The permission base
|
|
66
|
-
* layer fills only the classes the caller leaves unspecified: an explicit caller
|
|
67
|
-
* permission key wins per key and every non-permission key passes through
|
|
68
|
-
* untouched. A caller that supplies a non-object `permission` shorthand states
|
|
69
|
-
* the whole policy and keeps it.
|
|
70
|
-
*/
|
|
19
|
+
// v2.0.20 Agent.Info.default asks only for external_directory and env reads;
|
|
20
|
+
// question separately requests permission before awaiting its form reply.
|
|
21
|
+
const HEADLESS_PERMISSION_BASE = [
|
|
22
|
+
{ action: "external_directory", resource: "*", effect: "allow" },
|
|
23
|
+
{ action: "read", resource: "*", effect: "allow" },
|
|
24
|
+
{ action: "question", resource: "*", effect: "deny" },
|
|
25
|
+
];
|
|
71
26
|
export function composeHeadlessPermissionConfig(config) {
|
|
72
|
-
const
|
|
73
|
-
if (
|
|
74
|
-
|
|
75
|
-
|
|
27
|
+
const permissions = config?.permissions;
|
|
28
|
+
if (permissions !== undefined && !Array.isArray(permissions))
|
|
29
|
+
throw new Error("OpenCode v2 permissions must be a ruleset");
|
|
30
|
+
const actions = new Set((permissions ?? []).map((rule) => object(rule)?.action));
|
|
31
|
+
return {
|
|
32
|
+
...config,
|
|
33
|
+
permissions: [...HEADLESS_PERMISSION_BASE.filter((rule) => !actions.has(rule.action)), ...(permissions ?? [])],
|
|
34
|
+
};
|
|
76
35
|
}
|
|
77
|
-
|
|
36
|
+
/** A successful HTML fallback, or a JSON response from another API, is not readiness. */
|
|
37
|
+
export async function waitReady(json, signal) {
|
|
38
|
+
let last;
|
|
78
39
|
for (let attempt = 0; attempt < 100; attempt += 1) {
|
|
79
|
-
|
|
80
|
-
throw new Error("OpenCode startup aborted");
|
|
40
|
+
signal.throwIfAborted();
|
|
81
41
|
try {
|
|
82
|
-
await abortable(
|
|
42
|
+
const info = object(await abortable(json("/api/info"), signal));
|
|
43
|
+
if (typeof info?.version !== "string" ||
|
|
44
|
+
!info.version.startsWith("2.") ||
|
|
45
|
+
typeof info.pid !== "number" ||
|
|
46
|
+
!Array.isArray(info.urls) ||
|
|
47
|
+
typeof object(info.paths)?.tmp !== "string") {
|
|
48
|
+
throw new Error("OpenCode readiness requires native v2 server identity");
|
|
49
|
+
}
|
|
83
50
|
return;
|
|
84
51
|
}
|
|
85
|
-
catch {
|
|
52
|
+
catch (error) {
|
|
53
|
+
last = error;
|
|
54
|
+
signal.throwIfAborted();
|
|
86
55
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
87
56
|
}
|
|
88
57
|
}
|
|
89
|
-
throw new Error(
|
|
58
|
+
throw new Error(`OpenCode server did not become ready within 10000ms: ${String(last)}`);
|
|
90
59
|
}
|
|
91
60
|
async function availablePort() {
|
|
92
61
|
return await new Promise((resolve, reject) => {
|
|
@@ -105,15 +74,17 @@ async function availablePort() {
|
|
|
105
74
|
}
|
|
106
75
|
export async function loadOpencode(input) {
|
|
107
76
|
const { execution, cwd, signal, loader, onRuntime, requests } = input;
|
|
108
|
-
if (loader) {
|
|
109
|
-
const loaded = await abortable(loader(cwd, {
|
|
77
|
+
if (loader !== undefined) {
|
|
78
|
+
const loaded = await abortable(loader(cwd, {
|
|
79
|
+
...execution,
|
|
80
|
+
env: akumaExecutionEnvironment(process.env, execution.env, requests),
|
|
81
|
+
}, signal), signal);
|
|
110
82
|
let closing;
|
|
111
83
|
const runtime = {
|
|
112
84
|
client: loaded.client,
|
|
113
|
-
close:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
},
|
|
85
|
+
close: () => (closing ??= Promise.resolve()
|
|
86
|
+
.then(() => loaded.close?.())
|
|
87
|
+
.then(() => undefined)),
|
|
117
88
|
};
|
|
118
89
|
onRuntime?.(runtime);
|
|
119
90
|
try {
|
|
@@ -128,34 +99,56 @@ export async function loadOpencode(input) {
|
|
|
128
99
|
}
|
|
129
100
|
}
|
|
130
101
|
const port = await availablePort();
|
|
102
|
+
signal.throwIfAborted();
|
|
103
|
+
const password = execution.env?.OPENCODE_PASSWORD ??
|
|
104
|
+
execution.env?.OPENCODE_SERVER_PASSWORD ??
|
|
105
|
+
randomBytes(32).toString("base64url");
|
|
106
|
+
const config = composeHeadlessPermissionConfig(execution.config);
|
|
131
107
|
const owned = await spawnDetachedProcess({
|
|
132
108
|
argv: [execution.executable ?? "opencode", "serve", "--hostname", "127.0.0.1", "--port", String(port)],
|
|
133
109
|
cwd,
|
|
134
110
|
env: akumaExecutionEnvironment(process.env, {
|
|
135
|
-
OPENCODE_CONFIG_CONTENT: JSON.stringify(
|
|
111
|
+
OPENCODE_CONFIG_CONTENT: JSON.stringify(config),
|
|
136
112
|
...execution.env,
|
|
113
|
+
OPENCODE_PASSWORD: password,
|
|
114
|
+
OPENCODE_SERVER_PASSWORD: password,
|
|
137
115
|
}, requests),
|
|
138
116
|
log: `${cwd}/.opencode.log`,
|
|
139
117
|
});
|
|
140
|
-
const
|
|
141
|
-
const client = createOpencodeClient({ baseUrl: `http://127.0.0.1:${port}`, directory: cwd });
|
|
118
|
+
const native = createNativeClient(`http://127.0.0.1:${port}`, password);
|
|
142
119
|
let closing;
|
|
120
|
+
const options = { signal };
|
|
143
121
|
const runtime = {
|
|
144
|
-
client: {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
122
|
+
client: {
|
|
123
|
+
session: {
|
|
124
|
+
create: async ({ body }) => ({ data: await native.sdk.session.create(body, options) }),
|
|
125
|
+
get: async ({ id }) => ({ data: await native.sdk.session.get({ sessionID: id }, options) }),
|
|
126
|
+
switchModel: ({ id, model }) => native.sdk.session.switchModel({ sessionID: id, model }, options),
|
|
127
|
+
instructions: ({ id, text }) => native.sdk.session.instructions.entry.put({ sessionID: id, key: "keiyaku.system", value: text }, options),
|
|
128
|
+
active: async () => ({ data: await native.sdk.session.active(options) }),
|
|
129
|
+
fork: async ({ id, before }) => ({ data: await native.sdk.session.fork({ sessionID: id, before }, options) }),
|
|
130
|
+
interrupt: ({ id }) => native.sdk.session.interrupt({ sessionID: id, resume: false }),
|
|
131
|
+
prompt: async ({ id, body }) => ({
|
|
132
|
+
data: await native.sdk.session.prompt({ sessionID: id, ...body }, options),
|
|
133
|
+
}),
|
|
134
|
+
message: async ({ id, messageID }) => ({
|
|
135
|
+
data: await native.sdk.session.message.get({ sessionID: id, messageID }, options),
|
|
136
|
+
}),
|
|
137
|
+
},
|
|
138
|
+
event: { subscribe: ({ signal }) => native.subscribe(signal) },
|
|
148
139
|
},
|
|
140
|
+
close: () => (closing ??= Promise.all([native.close(), owned.terminate()]).then(() => undefined)),
|
|
149
141
|
};
|
|
150
142
|
onRuntime?.(runtime);
|
|
151
143
|
try {
|
|
152
|
-
await waitReady(
|
|
144
|
+
await waitReady(() => native.sdk.server.info({ signal }), signal);
|
|
145
|
+
signal.throwIfAborted();
|
|
146
|
+
return runtime;
|
|
153
147
|
}
|
|
154
148
|
catch (error) {
|
|
155
149
|
await runtime.close();
|
|
156
150
|
throw error;
|
|
157
151
|
}
|
|
158
|
-
return runtime;
|
|
159
152
|
}
|
|
160
153
|
export function coordinate(sessionId) {
|
|
161
154
|
return { sessionId };
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import type { AgentStreamEventPayload } from "@getpaseo/protocol/messages";
|
|
2
|
+
import type { AgentRuntimeInfo } from "@getpaseo/protocol/agent-types";
|
|
2
3
|
import { AgentEventChannel, type TurnResult } from "../../provider.js";
|
|
4
|
+
/**
|
|
5
|
+
* The runtime's own confirmation at establishment, reported as a native
|
|
6
|
+
* provider model id. The installed Paseo wire carries no mid-attempt model
|
|
7
|
+
* change on this timeline subscription, so a later re-selection is not
|
|
8
|
+
* observable here and is never invented.
|
|
9
|
+
*/
|
|
10
|
+
export declare function paseoRuntimeModel(info: AgentRuntimeInfo | undefined): string | undefined;
|
|
3
11
|
/** Live fragments belong to a canonical admitted turn, not agent-wide status. */
|
|
4
12
|
export declare class PaseoTurn {
|
|
5
13
|
readonly messageId: string;
|
|
@@ -1,4 +1,45 @@
|
|
|
1
1
|
import { AgentEventChannel, otherToolCall } from "../../provider.js";
|
|
2
|
+
import { stringValue } from "../decode.js";
|
|
3
|
+
/**
|
|
4
|
+
* The runtime's own confirmation at establishment, reported as a native
|
|
5
|
+
* provider model id. The installed Paseo wire carries no mid-attempt model
|
|
6
|
+
* change on this timeline subscription, so a later re-selection is not
|
|
7
|
+
* observable here and is never invented.
|
|
8
|
+
*/
|
|
9
|
+
export function paseoRuntimeModel(info) {
|
|
10
|
+
const model = info?.model;
|
|
11
|
+
return typeof model === "string" && model.trim().length > 0 ? model : undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The observed result text one native detail carries. A detail that observes no
|
|
15
|
+
* text stays absent; a present empty string remains observed and empty.
|
|
16
|
+
*/
|
|
17
|
+
function detailOutput(detail) {
|
|
18
|
+
switch (detail.type) {
|
|
19
|
+
case "shell":
|
|
20
|
+
return stringValue(detail.output);
|
|
21
|
+
case "read":
|
|
22
|
+
case "search":
|
|
23
|
+
return stringValue(detail.content);
|
|
24
|
+
case "fetch":
|
|
25
|
+
return stringValue(detail.result);
|
|
26
|
+
default:
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** The verified unified patch a completed edit detail observed, when it supplied one. */
|
|
31
|
+
function detailPatch(detail) {
|
|
32
|
+
return detail.type === "edit" ? stringValue(detail.unifiedDiff) : undefined;
|
|
33
|
+
}
|
|
34
|
+
/** Attach the observed patch to the one change its correlated path already named. */
|
|
35
|
+
function withObservedPatch(call, patch) {
|
|
36
|
+
if (call.kind !== "fileChange")
|
|
37
|
+
return call;
|
|
38
|
+
const change = call.changes[0];
|
|
39
|
+
if (patch === undefined || change === undefined || call.changes.length !== 1)
|
|
40
|
+
return call;
|
|
41
|
+
return { kind: "fileChange", changes: [{ ...change, patch }] };
|
|
42
|
+
}
|
|
2
43
|
function toolCall(item) {
|
|
3
44
|
const detail = item.detail;
|
|
4
45
|
switch (detail.type) {
|
|
@@ -25,18 +66,24 @@ function toolCall(item) {
|
|
|
25
66
|
function timelineNarration(item, calls) {
|
|
26
67
|
switch (item.type) {
|
|
27
68
|
case "tool_call": {
|
|
28
|
-
const
|
|
69
|
+
const base = calls.get(item.callId) ?? (item.status === "running" ? toolCall(item) : otherToolCall(item.name));
|
|
70
|
+
const call = item.status === "running" ? base : withObservedPatch(base, detailPatch(item.detail));
|
|
29
71
|
calls.set(item.callId, call);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
72
|
+
if (item.status === "running") {
|
|
73
|
+
return { type: "tool", phase: "started", id: item.callId, name: item.name, call };
|
|
74
|
+
}
|
|
75
|
+
const output = detailOutput(item.detail);
|
|
76
|
+
return {
|
|
77
|
+
type: "tool",
|
|
78
|
+
phase: "completed",
|
|
79
|
+
id: item.callId,
|
|
80
|
+
name: item.name,
|
|
81
|
+
call,
|
|
82
|
+
result: {
|
|
83
|
+
status: item.status === "completed" ? "ok" : "error",
|
|
84
|
+
...(output === undefined ? {} : { output }),
|
|
85
|
+
},
|
|
86
|
+
};
|
|
40
87
|
}
|
|
41
88
|
case "error":
|
|
42
89
|
case "notification":
|
|
@@ -5,8 +5,9 @@ import { z } from "zod";
|
|
|
5
5
|
import { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
|
6
6
|
import { bindWsHandler, createWebSocketTransportFactory, defaultWebSocketFactory, } from "@getpaseo/client/internal/daemon-client-websocket-transport";
|
|
7
7
|
import { abortable } from "../../abort.js";
|
|
8
|
+
import { launchPrompt, modelEvent, } from "../../provider.js";
|
|
8
9
|
import { AKUMA_REQUESTS_ENTRY_ENV, akumaExecutionEnvironment } from "../execution-environment.js";
|
|
9
|
-
import { PaseoTurn } from "./events.js";
|
|
10
|
+
import { PaseoTurn, paseoRuntimeModel } from "./events.js";
|
|
10
11
|
import { providerLifecycle } from "../lifecycle.js";
|
|
11
12
|
const configSchema = z
|
|
12
13
|
.object({
|
|
@@ -169,6 +170,11 @@ async function applyOptions(client, agent, input, config) {
|
|
|
169
170
|
(observed.agent.effectiveThinkingOptionId ?? observed.agent.runtimeInfo?.thinkingOptionId) !== effort) {
|
|
170
171
|
throw new Error(`Paseo effort selection was not confirmed: ${effort}`);
|
|
171
172
|
}
|
|
173
|
+
// The runtime's own answer names the model this agent will run, whether or
|
|
174
|
+
// not the caller requested one. The snapshot's top-level model may be a
|
|
175
|
+
// configured fallback rather than what the runtime loaded, so it is never
|
|
176
|
+
// runtime evidence; an unnamed runtime stays unknown and clears nothing.
|
|
177
|
+
return paseoRuntimeModel(observed.agent.runtimeInfo);
|
|
172
178
|
}
|
|
173
179
|
class PaseoDrive {
|
|
174
180
|
client;
|
|
@@ -216,18 +222,16 @@ class PaseoDrive {
|
|
|
216
222
|
}))
|
|
217
223
|
: await this.existingAgent(coordinate, input.signal);
|
|
218
224
|
this.agentId = agent.id;
|
|
219
|
-
await applyOptions(this.client, agent, input, config);
|
|
225
|
+
const startupModel = await applyOptions(this.client, agent, input, config);
|
|
220
226
|
input.signal.throwIfAborted();
|
|
221
227
|
const subscription = this.client.subscribeAgentTimeline(agent.id, (message) => this.receive(message));
|
|
222
228
|
await abortable(subscription.ready, input.signal);
|
|
223
229
|
this.turn.events.emit({ type: "session", coordinate: { sessionId: agent.id } });
|
|
230
|
+
if (startupModel !== undefined)
|
|
231
|
+
this.turn.events.emit(modelEvent(startupModel));
|
|
224
232
|
// Native steer may still replace work if a human wins the check/send race.
|
|
225
233
|
input.signal.throwIfAborted();
|
|
226
|
-
const
|
|
227
|
-
if (input.schemaJson !== undefined) {
|
|
228
|
-
parts.push("Respond with JSON matching this JSON Schema and no other text:", input.schemaJson);
|
|
229
|
-
}
|
|
230
|
-
const prompt = parts.filter((text) => text.length > 0).join("\n\n");
|
|
234
|
+
const prompt = launchPrompt(input.body, input.launchTells, input.schemaJson);
|
|
231
235
|
await setup(input.signal, () => {
|
|
232
236
|
this.submitted = true;
|
|
233
237
|
return this.client.sendMessage(agent.id, prompt, { messageId: this.turn.messageId, activeTurnBehavior: "steer" });
|
|
@@ -25,6 +25,8 @@ export declare const PI_EVENT_DISPOSITIONS: {
|
|
|
25
25
|
readonly turn_end: "drop";
|
|
26
26
|
readonly turn_start: "drop";
|
|
27
27
|
};
|
|
28
|
+
/** The model this native session will run, from its own selection. */
|
|
29
|
+
export declare function piSessionModel(selected: unknown): string | undefined;
|
|
28
30
|
export type PiEventState = {
|
|
29
31
|
answer: string;
|
|
30
32
|
assistantSeen: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { noteEvent, otherToolCall, unknownEvent } from "../../provider.js";
|
|
2
|
-
import { nonblank, positiveLine, searchToolCall } from "../decode.js";
|
|
1
|
+
import { modelEvent, noteEvent, otherToolCall, unknownEvent } from "../../provider.js";
|
|
2
|
+
import { nonblank, observedTextContent, positiveLine, searchToolCall } from "../decode.js";
|
|
3
3
|
import { diffstatFromUnifiedPatch } from "../unified-patch.js";
|
|
4
4
|
export const PI_EVENT_DISPOSITIONS = {
|
|
5
5
|
agent_end: "drop",
|
|
@@ -32,25 +32,25 @@ function record(value) {
|
|
|
32
32
|
: null;
|
|
33
33
|
}
|
|
34
34
|
function textContent(content) {
|
|
35
|
-
|
|
36
|
-
return content;
|
|
37
|
-
if (!Array.isArray(content))
|
|
38
|
-
return "";
|
|
39
|
-
return content
|
|
40
|
-
.map((block) => {
|
|
41
|
-
const value = record(block);
|
|
42
|
-
return value?.type === "text" && typeof value.text === "string" ? value.text : "";
|
|
43
|
-
})
|
|
44
|
-
.join("");
|
|
35
|
+
return observedTextContent(content) ?? "";
|
|
45
36
|
}
|
|
46
37
|
function hasTextContent(content) {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
return observedTextContent(content) !== undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Pi identifies a model as `<provider>/<id>`, the same convention its model
|
|
42
|
+
* option uses. The selected model and every recorded response carry both
|
|
43
|
+
* halves; a response without them names no model and stays unknown.
|
|
44
|
+
*/
|
|
45
|
+
function piModel(provider, id) {
|
|
46
|
+
const owner = nonblank(provider);
|
|
47
|
+
const name = nonblank(id);
|
|
48
|
+
return owner === undefined || name === undefined ? undefined : `${owner}/${name}`;
|
|
49
|
+
}
|
|
50
|
+
/** The model this native session will run, from its own selection. */
|
|
51
|
+
export function piSessionModel(selected) {
|
|
52
|
+
const value = record(selected);
|
|
53
|
+
return piModel(value?.provider, value?.id);
|
|
54
54
|
}
|
|
55
55
|
function runCall(name, value) {
|
|
56
56
|
return name === "bash" && typeof value.command === "string" ? { kind: "run", command: value.command } : undefined;
|
|
@@ -89,13 +89,14 @@ function refineEditCall(call, result) {
|
|
|
89
89
|
if (call.kind !== "fileChange")
|
|
90
90
|
return call;
|
|
91
91
|
const details = record(record(result)?.details);
|
|
92
|
-
|
|
92
|
+
const patch = typeof details?.patch === "string" ? details.patch : undefined;
|
|
93
|
+
if (patch === undefined)
|
|
93
94
|
return call;
|
|
94
|
-
const diffstat = diffstatFromUnifiedPatch(
|
|
95
|
+
const diffstat = diffstatFromUnifiedPatch(patch);
|
|
95
96
|
const change = call.changes[0];
|
|
96
|
-
if (
|
|
97
|
+
if (change === undefined)
|
|
97
98
|
return call;
|
|
98
|
-
return { kind: "fileChange", changes: [{ ...change, diffstat }] };
|
|
99
|
+
return { kind: "fileChange", changes: [{ ...change, ...(diffstat === undefined ? {} : { diffstat }), patch }] };
|
|
99
100
|
}
|
|
100
101
|
function toolCall(name, args) {
|
|
101
102
|
const value = record(args) ?? {};
|
|
@@ -121,6 +122,7 @@ function translateToolEnd(event, state) {
|
|
|
121
122
|
state.tools.delete(event.toolCallId);
|
|
122
123
|
const name = started?.name ?? event.toolName;
|
|
123
124
|
const call = started?.call ?? otherToolCall(name);
|
|
125
|
+
const output = observedTextContent(record(event.result)?.content);
|
|
124
126
|
return [
|
|
125
127
|
{
|
|
126
128
|
type: "tool",
|
|
@@ -128,7 +130,10 @@ function translateToolEnd(event, state) {
|
|
|
128
130
|
id: event.toolCallId,
|
|
129
131
|
name,
|
|
130
132
|
call: event.isError ? call : refineEditCall(call, event.result),
|
|
131
|
-
result: {
|
|
133
|
+
result: {
|
|
134
|
+
status: event.isError ? "error" : "ok",
|
|
135
|
+
...(output === undefined ? {} : { output }),
|
|
136
|
+
},
|
|
132
137
|
},
|
|
133
138
|
];
|
|
134
139
|
}
|
|
@@ -137,6 +142,9 @@ function translateMessage(event, state) {
|
|
|
137
142
|
if (message?.role !== "assistant")
|
|
138
143
|
return [];
|
|
139
144
|
const translated = [];
|
|
145
|
+
const model = piModel(message.provider, message.model);
|
|
146
|
+
if (model !== undefined)
|
|
147
|
+
translated.push(modelEvent(model));
|
|
140
148
|
if (Array.isArray(message.content)) {
|
|
141
149
|
for (const block of message.content) {
|
|
142
150
|
const value = record(block);
|