@agentex/agent 0.0.27 → 0.0.28
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/CHANGELOG.md +108 -0
- package/README.md +58 -7
- package/dist/derived.d.ts.map +1 -1
- package/dist/derived.js +99 -2
- package/dist/derived.js.map +1 -1
- package/dist/goals/controller.d.ts.map +1 -1
- package/dist/goals/controller.js +21 -0
- package/dist/goals/controller.js.map +1 -1
- package/dist/goals/normalize.d.ts +1 -0
- package/dist/goals/normalize.d.ts.map +1 -1
- package/dist/goals/normalize.js +5 -0
- package/dist/goals/normalize.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/claude/attach.d.ts.map +1 -1
- package/dist/providers/claude/attach.js +50 -9
- package/dist/providers/claude/attach.js.map +1 -1
- package/dist/providers/claude/index.d.ts.map +1 -1
- package/dist/providers/claude/index.js +8 -0
- package/dist/providers/claude/index.js.map +1 -1
- package/dist/providers/claude/session.d.ts +2 -0
- package/dist/providers/claude/session.d.ts.map +1 -1
- package/dist/providers/claude/session.js +17 -10
- package/dist/providers/claude/session.js.map +1 -1
- package/dist/providers/codex/attach.d.ts.map +1 -1
- package/dist/providers/codex/attach.js +40 -3
- package/dist/providers/codex/attach.js.map +1 -1
- package/dist/providers/codex/index.d.ts.map +1 -1
- package/dist/providers/codex/index.js +8 -0
- package/dist/providers/codex/index.js.map +1 -1
- package/dist/providers/codex/transcript.d.ts +3 -1
- package/dist/providers/codex/transcript.d.ts.map +1 -1
- package/dist/providers/codex/transcript.js +3 -1
- package/dist/providers/codex/transcript.js.map +1 -1
- package/dist/providers/cursor/discovery.d.ts +6 -0
- package/dist/providers/cursor/discovery.d.ts.map +1 -0
- package/dist/providers/cursor/discovery.js +118 -0
- package/dist/providers/cursor/discovery.js.map +1 -0
- package/dist/providers/cursor/execute.d.ts.map +1 -1
- package/dist/providers/cursor/execute.js +83 -45
- package/dist/providers/cursor/execute.js.map +1 -1
- package/dist/providers/cursor/index.d.ts.map +1 -1
- package/dist/providers/cursor/index.js +17 -4
- package/dist/providers/cursor/index.js.map +1 -1
- package/dist/providers/cursor/parse.js +1 -1
- package/dist/providers/cursor/parse.js.map +1 -1
- package/dist/providers/cursor/probe.d.ts +3 -0
- package/dist/providers/cursor/probe.d.ts.map +1 -0
- package/dist/providers/cursor/probe.js +89 -0
- package/dist/providers/cursor/probe.js.map +1 -0
- package/dist/providers/cursor/runtime.d.ts +4 -0
- package/dist/providers/cursor/runtime.d.ts.map +1 -0
- package/dist/providers/cursor/runtime.js +12 -0
- package/dist/providers/cursor/runtime.js.map +1 -0
- package/dist/providers/cursor/session.d.ts +3 -0
- package/dist/providers/cursor/session.d.ts.map +1 -0
- package/dist/providers/cursor/session.js +14 -0
- package/dist/providers/cursor/session.js.map +1 -0
- package/dist/providers/opencode/client.d.ts +14 -0
- package/dist/providers/opencode/client.d.ts.map +1 -0
- package/dist/providers/opencode/client.js +40 -0
- package/dist/providers/opencode/client.js.map +1 -0
- package/dist/providers/opencode/discovery.d.ts +4 -0
- package/dist/providers/opencode/discovery.d.ts.map +1 -0
- package/dist/providers/opencode/discovery.js +68 -0
- package/dist/providers/opencode/discovery.js.map +1 -0
- package/dist/providers/opencode/execute.d.ts.map +1 -1
- package/dist/providers/opencode/execute.js +219 -164
- package/dist/providers/opencode/execute.js.map +1 -1
- package/dist/providers/opencode/history.d.ts +24 -0
- package/dist/providers/opencode/history.d.ts.map +1 -0
- package/dist/providers/opencode/history.js +242 -0
- package/dist/providers/opencode/history.js.map +1 -0
- package/dist/providers/opencode/http-session.d.ts.map +1 -1
- package/dist/providers/opencode/http-session.js +333 -49
- package/dist/providers/opencode/http-session.js.map +1 -1
- package/dist/providers/opencode/index.d.ts.map +1 -1
- package/dist/providers/opencode/index.js +26 -3
- package/dist/providers/opencode/index.js.map +1 -1
- package/dist/providers/opencode/manager.d.ts +11 -0
- package/dist/providers/opencode/manager.d.ts.map +1 -0
- package/dist/providers/opencode/manager.js +240 -0
- package/dist/providers/opencode/manager.js.map +1 -0
- package/dist/providers/opencode/modes.d.ts +3 -0
- package/dist/providers/opencode/modes.d.ts.map +1 -0
- package/dist/providers/opencode/modes.js +30 -0
- package/dist/providers/opencode/modes.js.map +1 -0
- package/dist/providers/opencode/parse.d.ts.map +1 -1
- package/dist/providers/opencode/parse.js +2 -22
- package/dist/providers/opencode/parse.js.map +1 -1
- package/dist/providers/opencode/probe.d.ts +3 -0
- package/dist/providers/opencode/probe.d.ts.map +1 -0
- package/dist/providers/opencode/probe.js +107 -0
- package/dist/providers/opencode/probe.js.map +1 -0
- package/dist/providers/opencode/runtime.d.ts +8 -0
- package/dist/providers/opencode/runtime.d.ts.map +1 -0
- package/dist/providers/opencode/runtime.js +12 -0
- package/dist/providers/opencode/runtime.js.map +1 -0
- package/dist/providers/opencode/server.d.ts +18 -3
- package/dist/providers/opencode/server.d.ts.map +1 -1
- package/dist/providers/opencode/server.js +0 -0
- package/dist/providers/opencode/server.js.map +1 -1
- package/dist/providers/opencode/skill-config.d.ts +11 -0
- package/dist/providers/opencode/skill-config.d.ts.map +1 -0
- package/dist/providers/opencode/skill-config.js +57 -0
- package/dist/providers/opencode/skill-config.js.map +1 -0
- package/dist/sessions/exec-backed.d.ts +10 -0
- package/dist/sessions/exec-backed.d.ts.map +1 -0
- package/dist/sessions/exec-backed.js +141 -0
- package/dist/sessions/exec-backed.js.map +1 -0
- package/dist/sessions/history.d.ts +4 -0
- package/dist/sessions/history.d.ts.map +1 -0
- package/dist/sessions/history.js +59 -0
- package/dist/sessions/history.js.map +1 -0
- package/dist/sessions/index.d.ts +3 -0
- package/dist/sessions/index.d.ts.map +1 -1
- package/dist/sessions/index.js +2 -0
- package/dist/sessions/index.js.map +1 -1
- package/dist/types.d.ts +147 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/auth.d.ts.map +1 -1
- package/dist/utils/auth.js +36 -6
- package/dist/utils/auth.js.map +1 -1
- package/dist/utils/binary.d.ts.map +1 -1
- package/dist/utils/binary.js +3 -0
- package/dist/utils/binary.js.map +1 -1
- package/dist/utils/endpoint.js +3 -3
- package/dist/utils/endpoint.js.map +1 -1
- package/package.json +4 -2
- package/src/derived.ts +126 -3
- package/src/goals/controller.ts +17 -0
- package/src/goals/normalize.ts +5 -0
- package/src/index.ts +13 -0
- package/src/providers/claude/attach.ts +48 -6
- package/src/providers/claude/index.ts +8 -0
- package/src/providers/claude/session.ts +15 -10
- package/src/providers/codex/attach.ts +48 -3
- package/src/providers/codex/index.ts +8 -0
- package/src/providers/codex/transcript.ts +5 -1
- package/src/providers/cursor/discovery.ts +114 -0
- package/src/providers/cursor/execute.ts +67 -30
- package/src/providers/cursor/index.ts +19 -5
- package/src/providers/cursor/parse.ts +1 -1
- package/src/providers/cursor/probe.ts +90 -0
- package/src/providers/cursor/runtime.ts +11 -0
- package/src/providers/cursor/session.ts +15 -0
- package/src/providers/opencode/client.ts +44 -0
- package/src/providers/opencode/discovery.ts +69 -0
- package/src/providers/opencode/execute.ts +64 -12
- package/src/providers/opencode/history.ts +270 -0
- package/src/providers/opencode/http-session.ts +319 -50
- package/src/providers/opencode/index.ts +26 -3
- package/src/providers/opencode/manager.ts +269 -0
- package/src/providers/opencode/modes.ts +31 -0
- package/src/providers/opencode/parse.ts +2 -22
- package/src/providers/opencode/probe.ts +113 -0
- package/src/providers/opencode/runtime.ts +13 -0
- package/src/providers/opencode/server.ts +0 -0
- package/src/providers/opencode/skill-config.ts +64 -0
- package/src/sessions/exec-backed.ts +169 -0
- package/src/sessions/history.ts +76 -0
- package/src/sessions/index.ts +3 -0
- package/src/types.ts +158 -3
- package/src/utils/auth.ts +35 -6
- package/src/utils/binary.ts +3 -0
- package/src/utils/endpoint.ts +3 -3
|
@@ -3,7 +3,6 @@ import type { ExecutionContext, ExecutionResult } from "../../types.js";
|
|
|
3
3
|
import { findBinary } from "../../utils/binary.js";
|
|
4
4
|
import { buildEnv, ensurePathInEnv } from "../../utils/env.js";
|
|
5
5
|
import { runChildProcess, deriveErrorCode } from "../../utils/process.js";
|
|
6
|
-
import { injectHomeSkills } from "../../utils/skills.js";
|
|
7
6
|
import { resolveInstructions } from "../../utils/instructions.js";
|
|
8
7
|
import { prepareWorkspace } from "../../utils/workspace.js";
|
|
9
8
|
import type { PreparedWorkspace } from "../../utils/workspace.js";
|
|
@@ -14,6 +13,7 @@ import {
|
|
|
14
13
|
isOpenCodeUnknownSessionError,
|
|
15
14
|
isOpenCodeAuthRequired,
|
|
16
15
|
} from "./parse.js";
|
|
16
|
+
import { prepareOpenCodeSkillConfig } from "./skill-config.js";
|
|
17
17
|
|
|
18
18
|
export async function executeOpenCodeProvider(ctx: ExecutionContext): Promise<ExecutionResult> {
|
|
19
19
|
const runId = ctx.runId ?? uuidv7();
|
|
@@ -29,6 +29,31 @@ export async function executeOpenCodeProvider(ctx: ExecutionContext): Promise<Ex
|
|
|
29
29
|
resolvedBinary = await findBinary("opencode", config.command);
|
|
30
30
|
} catch (err) {
|
|
31
31
|
const errorMessage = err instanceof Error ? err.message : "Binary not found";
|
|
32
|
+
const completedAt = new Date().toISOString();
|
|
33
|
+
if (ctx.onEvent) {
|
|
34
|
+
try {
|
|
35
|
+
await ctx.onEvent({
|
|
36
|
+
type: "result",
|
|
37
|
+
text: errorMessage,
|
|
38
|
+
costUsd: null,
|
|
39
|
+
isError: true,
|
|
40
|
+
stopReason: null,
|
|
41
|
+
terminalReason: "failed",
|
|
42
|
+
numTurns: null,
|
|
43
|
+
durationMs: new Date(completedAt).getTime() - new Date(startedAt).getTime(),
|
|
44
|
+
timestamp: completedAt,
|
|
45
|
+
providerType: "opencode",
|
|
46
|
+
sessionId: null,
|
|
47
|
+
messageId: null,
|
|
48
|
+
eventId: null,
|
|
49
|
+
turnId: null,
|
|
50
|
+
parentToolCallId: null,
|
|
51
|
+
raw: {},
|
|
52
|
+
});
|
|
53
|
+
} catch {
|
|
54
|
+
// A host event handler cannot fail the execution.
|
|
55
|
+
}
|
|
56
|
+
}
|
|
32
57
|
ctx.onLifecycle?.({ phase: "error", message: errorMessage });
|
|
33
58
|
return {
|
|
34
59
|
runId,
|
|
@@ -36,7 +61,7 @@ export async function executeOpenCodeProvider(ctx: ExecutionContext): Promise<Ex
|
|
|
36
61
|
signal: null,
|
|
37
62
|
status: "failed" as const,
|
|
38
63
|
startedAt,
|
|
39
|
-
completedAt
|
|
64
|
+
completedAt,
|
|
40
65
|
durationMs: Date.now() - new Date(startedAt).getTime(),
|
|
41
66
|
errorMessage,
|
|
42
67
|
errorCode: "binary_not_found",
|
|
@@ -59,24 +84,20 @@ export async function executeOpenCodeProvider(ctx: ExecutionContext): Promise<Ex
|
|
|
59
84
|
}
|
|
60
85
|
|
|
61
86
|
// 3. Build env
|
|
62
|
-
const
|
|
63
|
-
ensurePathInEnv(
|
|
87
|
+
const baseEnv = buildEnv(ctx.env);
|
|
88
|
+
ensurePathInEnv(baseEnv);
|
|
64
89
|
|
|
65
90
|
// 3. Resolve instructions
|
|
66
91
|
ctx.onLifecycle?.({ phase: "preparing", step: "instructions" });
|
|
67
92
|
const instructions = await resolveInstructions(config.instructionsFile);
|
|
68
93
|
const fullPrompt = instructions ? `${instructions}\n\n${ctx.prompt}` : ctx.prompt;
|
|
69
94
|
|
|
70
|
-
// 4. Inject skills
|
|
95
|
+
// 4. Inject skills through an isolated OpenCode config directory.
|
|
71
96
|
ctx.onLifecycle?.({ phase: "preparing", step: "skills" });
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
await injectHomeSkills(config.skillDirs, "opencode");
|
|
75
|
-
} catch {
|
|
76
|
-
// Non-fatal
|
|
77
|
-
}
|
|
78
|
-
}
|
|
97
|
+
const skillConfig = await prepareOpenCodeSkillConfig(baseEnv, config.skillDirs);
|
|
98
|
+
const env = skillConfig.env;
|
|
79
99
|
|
|
100
|
+
try {
|
|
80
101
|
// 4. Determine session resume
|
|
81
102
|
const sessionParams = ctx.sessionParams ?? null;
|
|
82
103
|
const sessionId = (() => {
|
|
@@ -96,6 +117,9 @@ export async function executeOpenCodeProvider(ctx: ExecutionContext): Promise<Ex
|
|
|
96
117
|
const args = [...resolvedBinary.prefixArgs, "run", "--format", "json"];
|
|
97
118
|
if (resumeSessionId) args.push("--session", resumeSessionId);
|
|
98
119
|
if (model) args.push("--model", model);
|
|
120
|
+
if (config.modelVariant) args.push("--variant", config.modelVariant);
|
|
121
|
+
const agent = config.planMode ? "plan" : (config.modeId ?? config.mode);
|
|
122
|
+
if (agent) args.push("--agent", agent);
|
|
99
123
|
if (config.extraArgs) args.push(...config.extraArgs);
|
|
100
124
|
return args;
|
|
101
125
|
};
|
|
@@ -217,6 +241,31 @@ export async function executeOpenCodeProvider(ctx: ExecutionContext): Promise<Ex
|
|
|
217
241
|
: (errorCode || errorMessage) ? "failed" as const
|
|
218
242
|
: "completed" as const;
|
|
219
243
|
|
|
244
|
+
if (ctx.onEvent) {
|
|
245
|
+
try {
|
|
246
|
+
await ctx.onEvent({
|
|
247
|
+
type: "result",
|
|
248
|
+
text: parsed.summary ?? errorMessage ?? "",
|
|
249
|
+
costUsd: parsed.costUsd,
|
|
250
|
+
isError: status !== "completed",
|
|
251
|
+
stopReason: null,
|
|
252
|
+
terminalReason: status,
|
|
253
|
+
numTurns: null,
|
|
254
|
+
durationMs: new Date(completedAt).getTime() - new Date(startedAt).getTime(),
|
|
255
|
+
timestamp: completedAt,
|
|
256
|
+
providerType: "opencode",
|
|
257
|
+
sessionId: resolvedSessionId,
|
|
258
|
+
messageId: null,
|
|
259
|
+
eventId: null,
|
|
260
|
+
turnId: null,
|
|
261
|
+
parentToolCallId: null,
|
|
262
|
+
raw: {},
|
|
263
|
+
});
|
|
264
|
+
} catch {
|
|
265
|
+
// A host event handler cannot fail the execution.
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
220
269
|
if (status === "completed") {
|
|
221
270
|
ctx.onLifecycle?.({ phase: "completed" });
|
|
222
271
|
} else if (status === "aborted") {
|
|
@@ -248,4 +297,7 @@ export async function executeOpenCodeProvider(ctx: ExecutionContext): Promise<Ex
|
|
|
248
297
|
raw: null,
|
|
249
298
|
workspace,
|
|
250
299
|
};
|
|
300
|
+
} finally {
|
|
301
|
+
await skillConfig.cleanup();
|
|
302
|
+
}
|
|
251
303
|
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AttachOptions,
|
|
3
|
+
HistoryAttachment,
|
|
4
|
+
HistoryCatchUpOptions,
|
|
5
|
+
HistoryCatchUpYield,
|
|
6
|
+
HistoryCheckpoint,
|
|
7
|
+
LastTurnStatus,
|
|
8
|
+
SessionRecord,
|
|
9
|
+
StreamEvent,
|
|
10
|
+
} from "../../types.js";
|
|
11
|
+
import { assertSessionRecord, createSessionRecord, MalformedSessionRecordError } from "../../sessions/record.js";
|
|
12
|
+
import { acquireOpenCodeRuntime } from "./runtime.js";
|
|
13
|
+
import { opencodeSessionCodec } from "./codec.js";
|
|
14
|
+
import { createOpenCodeSession } from "./http-session.js";
|
|
15
|
+
import { mapOpenCodePart, mapOpenCodeToolCall, type OcBaseInfo } from "./event-parse.js";
|
|
16
|
+
|
|
17
|
+
const PAGE_SIZE = 100;
|
|
18
|
+
const MAX_PAGES = 100;
|
|
19
|
+
const MAX_MESSAGES = 10_000;
|
|
20
|
+
const MAX_BYTES = 25 * 1024 * 1024;
|
|
21
|
+
const CHECKPOINT_KIND = "opencode:message-part:v1";
|
|
22
|
+
|
|
23
|
+
export class OpenCodeHistoryCheckpointNotFoundError extends Error {
|
|
24
|
+
readonly code = "history_checkpoint_not_found";
|
|
25
|
+
constructor() {
|
|
26
|
+
super("The OpenCode history checkpoint is no longer present");
|
|
27
|
+
this.name = "OpenCodeHistoryCheckpointNotFoundError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class OpenCodeHistoryResyncLimitError extends Error {
|
|
32
|
+
readonly code = "history_resync_limit";
|
|
33
|
+
constructor() {
|
|
34
|
+
super("OpenCode history exceeded the bounded resync limit");
|
|
35
|
+
this.name = "OpenCodeHistoryResyncLimitError";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface DecodedCheckpoint {
|
|
40
|
+
messageId: string;
|
|
41
|
+
partId: string;
|
|
42
|
+
ordinal: number | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface MessageEnvelope {
|
|
46
|
+
info?: Record<string, unknown>;
|
|
47
|
+
parts?: unknown[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function rec(value: unknown): Record<string, unknown> | null {
|
|
51
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
52
|
+
? value as Record<string, unknown>
|
|
53
|
+
: null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function string(value: unknown): string | null {
|
|
57
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function decodeCheckpoint(checkpoint: HistoryCheckpoint | undefined): DecodedCheckpoint | null {
|
|
61
|
+
if (!checkpoint) return null;
|
|
62
|
+
if (checkpoint.kind !== CHECKPOINT_KIND) throw new OpenCodeHistoryCheckpointNotFoundError();
|
|
63
|
+
const value = rec(checkpoint.value);
|
|
64
|
+
const messageId = string(value?.["messageId"]);
|
|
65
|
+
const partId = string(value?.["partId"]);
|
|
66
|
+
if (!messageId || !partId) throw new OpenCodeHistoryCheckpointNotFoundError();
|
|
67
|
+
const ordinal = typeof value?.["ordinal"] === "number" && Number.isInteger(value["ordinal"])
|
|
68
|
+
? value["ordinal"] as number
|
|
69
|
+
: null;
|
|
70
|
+
return { messageId, partId, ordinal };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function checkpoint(messageId: string, partId: string, ordinal: number): HistoryCheckpoint {
|
|
74
|
+
return { kind: CHECKPOINT_KIND, value: { messageId, partId, ordinal } };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function historicalEvents(message: MessageEnvelope, sessionId: string): Array<{ event: StreamEvent; partId: string }> {
|
|
78
|
+
const info = message.info ?? {};
|
|
79
|
+
if (info["role"] !== "assistant") return [];
|
|
80
|
+
const messageId = string(info["id"]);
|
|
81
|
+
const timestamp = new Date(
|
|
82
|
+
typeof rec(info["time"])?.["created"] === "number"
|
|
83
|
+
? rec(info["time"])!["created"] as number
|
|
84
|
+
: Date.now(),
|
|
85
|
+
).toISOString();
|
|
86
|
+
const base: OcBaseInfo = { provider: "opencode", sessionId, timestamp };
|
|
87
|
+
const events: Array<{ event: StreamEvent; partId: string }> = [];
|
|
88
|
+
for (const rawPart of message.parts ?? []) {
|
|
89
|
+
const part = rec(rawPart);
|
|
90
|
+
const partId = string(part?.["id"]);
|
|
91
|
+
if (!part || !partId) continue;
|
|
92
|
+
if (part["type"] === "tool") {
|
|
93
|
+
events.push({ event: mapOpenCodeToolCall(part, base), partId });
|
|
94
|
+
const result = mapOpenCodePart(part, base);
|
|
95
|
+
if (result?.type === "tool_result") events.push({ event: result, partId });
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const event = mapOpenCodePart(part, base);
|
|
99
|
+
if (event) events.push({ event, partId });
|
|
100
|
+
}
|
|
101
|
+
const lastPartId = events.at(-1)?.partId;
|
|
102
|
+
if (messageId && lastPartId) {
|
|
103
|
+
const error = info["error"] != null;
|
|
104
|
+
events.push({
|
|
105
|
+
partId: lastPartId,
|
|
106
|
+
event: {
|
|
107
|
+
type: "result",
|
|
108
|
+
text: "",
|
|
109
|
+
costUsd: typeof info["cost"] === "number" ? info["cost"] : null,
|
|
110
|
+
isError: error,
|
|
111
|
+
stopReason: null,
|
|
112
|
+
terminalReason: error ? "failed" : "completed",
|
|
113
|
+
numTurns: 1,
|
|
114
|
+
durationMs: null,
|
|
115
|
+
timestamp,
|
|
116
|
+
providerType: "opencode",
|
|
117
|
+
sessionId,
|
|
118
|
+
messageId,
|
|
119
|
+
eventId: `${messageId}:result`,
|
|
120
|
+
turnId: null,
|
|
121
|
+
parentToolCallId: null,
|
|
122
|
+
raw: info,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
return events;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function collectHistory(
|
|
130
|
+
client: import("./client.js").OpenCodeClient,
|
|
131
|
+
sessionId: string,
|
|
132
|
+
options: HistoryCatchUpOptions | undefined,
|
|
133
|
+
): Promise<Array<{ event: StreamEvent; checkpoint: HistoryCheckpoint; eventId: string | null }>> {
|
|
134
|
+
const after = options?.mode === "bounded_full_resync" ? null : decodeCheckpoint(options?.after);
|
|
135
|
+
const pages: MessageEnvelope[][] = [];
|
|
136
|
+
let before: string | null = null;
|
|
137
|
+
let foundMessage = after === null;
|
|
138
|
+
let collectedMessages = 0;
|
|
139
|
+
let collectedBytes = 0;
|
|
140
|
+
|
|
141
|
+
for (let pageIndex = 0; pageIndex < MAX_PAGES; pageIndex += 1) {
|
|
142
|
+
const query = new URLSearchParams({ limit: String(PAGE_SIZE) });
|
|
143
|
+
if (before) query.set("before", before);
|
|
144
|
+
const response = await client.request(`/session/${encodeURIComponent(sessionId)}/message?${query}`);
|
|
145
|
+
if (response.status === 404) return [];
|
|
146
|
+
if (!response.ok) throw new Error(`OpenCode history request failed (${response.status})`);
|
|
147
|
+
const text = await response.text();
|
|
148
|
+
collectedBytes += Buffer.byteLength(text);
|
|
149
|
+
const parsed = JSON.parse(text) as unknown;
|
|
150
|
+
const page = Array.isArray(parsed)
|
|
151
|
+
? parsed.map((value) => rec(value) as MessageEnvelope).filter(Boolean)
|
|
152
|
+
: [];
|
|
153
|
+
collectedMessages += page.length;
|
|
154
|
+
if (collectedBytes > MAX_BYTES || collectedMessages > MAX_MESSAGES) {
|
|
155
|
+
throw new OpenCodeHistoryResyncLimitError();
|
|
156
|
+
}
|
|
157
|
+
pages.push(page);
|
|
158
|
+
if (after && page.some((message) => string(message.info?.["id"]) === after.messageId)) {
|
|
159
|
+
foundMessage = true;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
if (page.length < PAGE_SIZE) break;
|
|
163
|
+
const next = response.headers.get("x-next-cursor");
|
|
164
|
+
if (!next || next === before) break;
|
|
165
|
+
before = next;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (!foundMessage) throw new OpenCodeHistoryCheckpointNotFoundError();
|
|
169
|
+
if (pages.length === MAX_PAGES && pages.at(-1)?.length === PAGE_SIZE) {
|
|
170
|
+
throw new OpenCodeHistoryResyncLimitError();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const messages = pages.reverse().flat();
|
|
174
|
+
const output: Array<{ event: StreamEvent; checkpoint: HistoryCheckpoint; eventId: string | null }> = [];
|
|
175
|
+
let passedCheckpoint = after === null;
|
|
176
|
+
let foundPart = after === null;
|
|
177
|
+
for (const message of messages) {
|
|
178
|
+
const messageId = string(message.info?.["id"]);
|
|
179
|
+
if (!messageId) continue;
|
|
180
|
+
const events = historicalEvents(message, sessionId);
|
|
181
|
+
const partOrdinals = new Map<string, number>();
|
|
182
|
+
for (const item of events) {
|
|
183
|
+
const ordinal = partOrdinals.get(item.partId) ?? 0;
|
|
184
|
+
partOrdinals.set(item.partId, ordinal + 1);
|
|
185
|
+
if (!passedCheckpoint) {
|
|
186
|
+
if (
|
|
187
|
+
foundPart
|
|
188
|
+
&& after!.ordinal === null
|
|
189
|
+
&& messageId === after!.messageId
|
|
190
|
+
&& item.partId === after!.partId
|
|
191
|
+
) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (
|
|
195
|
+
messageId === after!.messageId
|
|
196
|
+
&& item.partId === after!.partId
|
|
197
|
+
&& (after!.ordinal === null || ordinal === after!.ordinal)
|
|
198
|
+
) {
|
|
199
|
+
foundPart = true;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (foundPart) passedCheckpoint = true;
|
|
203
|
+
else continue;
|
|
204
|
+
}
|
|
205
|
+
output.push({
|
|
206
|
+
event: item.event,
|
|
207
|
+
checkpoint: checkpoint(messageId, item.partId, ordinal),
|
|
208
|
+
eventId: item.event.eventId,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
if (foundPart && messageId === after?.messageId) passedCheckpoint = true;
|
|
212
|
+
}
|
|
213
|
+
if (!foundPart) throw new OpenCodeHistoryCheckpointNotFoundError();
|
|
214
|
+
return output;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export async function attachOpenCodeHistory(
|
|
218
|
+
record: SessionRecord,
|
|
219
|
+
opts?: AttachOptions,
|
|
220
|
+
): Promise<HistoryAttachment> {
|
|
221
|
+
assertSessionRecord(record);
|
|
222
|
+
if (record.providerType !== "opencode") {
|
|
223
|
+
throw new MalformedSessionRecordError(
|
|
224
|
+
`opencode history attach requires providerType "opencode"; got ${JSON.stringify(record.providerType)}`,
|
|
225
|
+
"providerType",
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
const params = opencodeSessionCodec.deserialize(record.params);
|
|
229
|
+
if (!params) throw new MalformedSessionRecordError("opencode history record has no session id", "params");
|
|
230
|
+
const sessionId = params["sessionId"] as string;
|
|
231
|
+
const cwd = typeof params["cwd"] === "string" ? params["cwd"] : record.cwd ?? process.cwd();
|
|
232
|
+
const runtime = await acquireOpenCodeRuntime({ cwd, env: opts?.env, config: opts?.config });
|
|
233
|
+
let lastTurn: LastTurnStatus = "unknown";
|
|
234
|
+
try {
|
|
235
|
+
const response = await runtime.server.client.request(`/session/${encodeURIComponent(sessionId)}/message?limit=1`);
|
|
236
|
+
if (response.ok) {
|
|
237
|
+
const messages = await response.json() as MessageEnvelope[];
|
|
238
|
+
const info = messages.at(-1)?.info;
|
|
239
|
+
lastTurn = info
|
|
240
|
+
? (info["finish"] && !info["error"] ? "completed" : "interrupted")
|
|
241
|
+
: "unknown";
|
|
242
|
+
}
|
|
243
|
+
} catch {
|
|
244
|
+
lastTurn = "unknown";
|
|
245
|
+
}
|
|
246
|
+
const normalized = createSessionRecord({
|
|
247
|
+
providerType: "opencode",
|
|
248
|
+
params: { sessionId, cwd },
|
|
249
|
+
cwd,
|
|
250
|
+
displayId: sessionId,
|
|
251
|
+
});
|
|
252
|
+
return {
|
|
253
|
+
record: normalized,
|
|
254
|
+
historySource: { kind: "service", description: "OpenCode authenticated session message history" },
|
|
255
|
+
lastTurn,
|
|
256
|
+
catchUp(options) {
|
|
257
|
+
return {
|
|
258
|
+
async *[Symbol.asyncIterator](): AsyncIterator<HistoryCatchUpYield> {
|
|
259
|
+
for (const yielded of await collectHistory(runtime.server.client, sessionId, options)) yield yielded;
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
resume(ctx) {
|
|
264
|
+
return createOpenCodeSession({ ...ctx, cwd: ctx?.cwd ?? cwd, sessionParams: params });
|
|
265
|
+
},
|
|
266
|
+
async close() {
|
|
267
|
+
runtime.server.release();
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
}
|