@agentex/agent 0.0.27 → 0.0.29
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 +138 -0
- package/README.md +98 -8
- 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/history/fs.d.ts +36 -0
- package/dist/history/fs.d.ts.map +1 -0
- package/dist/history/fs.js +266 -0
- package/dist/history/fs.js.map +1 -0
- package/dist/history/index.d.ts +3 -0
- package/dist/history/index.d.ts.map +1 -0
- package/dist/history/index.js +2 -0
- package/dist/history/index.js.map +1 -0
- package/dist/history/types.d.ts +75 -0
- package/dist/history/types.d.ts.map +1 -0
- package/dist/history/types.js +9 -0
- package/dist/history/types.js.map +1 -0
- package/dist/index.d.ts +3 -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/history.d.ts +3 -0
- package/dist/providers/claude/history.d.ts.map +1 -0
- package/dist/providers/claude/history.js +248 -0
- package/dist/providers/claude/history.js.map +1 -0
- package/dist/providers/claude/index.d.ts.map +1 -1
- package/dist/providers/claude/index.js +26 -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 +2 -2
- package/dist/providers/codex/attach.d.ts.map +1 -1
- package/dist/providers/codex/attach.js +44 -7
- package/dist/providers/codex/attach.js.map +1 -1
- package/dist/providers/codex/history.d.ts +3 -0
- package/dist/providers/codex/history.d.ts.map +1 -0
- package/dist/providers/codex/history.js +350 -0
- package/dist/providers/codex/history.js.map +1 -0
- package/dist/providers/codex/index.d.ts.map +1 -1
- package/dist/providers/codex/index.js +26 -0
- package/dist/providers/codex/index.js.map +1 -1
- package/dist/providers/codex/transcript-normalize.d.ts +3 -2
- package/dist/providers/codex/transcript-normalize.d.ts.map +1 -1
- package/dist/providers/codex/transcript-normalize.js +4 -3
- package/dist/providers/codex/transcript-normalize.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 +152 -6
- 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 +10 -2
- package/src/derived.ts +126 -3
- package/src/goals/controller.ts +17 -0
- package/src/goals/normalize.ts +5 -0
- package/src/history/fs.ts +307 -0
- package/src/history/index.ts +18 -0
- package/src/history/types.ts +104 -0
- package/src/index.ts +31 -0
- package/src/providers/claude/attach.ts +48 -6
- package/src/providers/claude/history.ts +290 -0
- package/src/providers/claude/index.ts +26 -0
- package/src/providers/claude/session.ts +15 -10
- package/src/providers/codex/attach.ts +52 -7
- package/src/providers/codex/history.ts +399 -0
- package/src/providers/codex/index.ts +26 -0
- package/src/providers/codex/transcript-normalize.ts +4 -3
- 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 +163 -4
- package/src/utils/auth.ts +35 -6
- package/src/utils/binary.ts +3 -0
- package/src/utils/endpoint.ts +3 -3
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AgentSession,
|
|
3
|
+
CancelResult,
|
|
4
|
+
ClearGoalResult,
|
|
5
|
+
ExecutionContext,
|
|
6
|
+
ExecutionResult,
|
|
7
|
+
GoalOptions,
|
|
8
|
+
GoalState,
|
|
9
|
+
ProviderCapabilities,
|
|
10
|
+
SendHandle,
|
|
11
|
+
SendOptions,
|
|
12
|
+
SessionCodec,
|
|
13
|
+
SessionContext,
|
|
14
|
+
SessionState,
|
|
15
|
+
SetGoalResult,
|
|
16
|
+
StopTaskResult,
|
|
17
|
+
StreamEvent,
|
|
18
|
+
TurnResult,
|
|
19
|
+
} from "../types.js";
|
|
20
|
+
import { GoalController, EMULATED_GOAL_CAPABILITY } from "../goals/index.js";
|
|
21
|
+
import { uuidv7 } from "../utils/uuid.js";
|
|
22
|
+
|
|
23
|
+
export interface ExecBackedSessionOptions {
|
|
24
|
+
providerType: string;
|
|
25
|
+
execute(ctx: ExecutionContext): Promise<ExecutionResult>;
|
|
26
|
+
sessionCodec: SessionCodec;
|
|
27
|
+
ctx: SessionContext;
|
|
28
|
+
capabilities?: Pick<ProviderCapabilities, "goals">;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function turnResult(result: ExecutionResult): TurnResult {
|
|
32
|
+
return {
|
|
33
|
+
summary: result.summary,
|
|
34
|
+
...(result.usage ? { usage: result.usage } : {}),
|
|
35
|
+
costUsd: result.costUsd,
|
|
36
|
+
status: result.status === "blocked" ? "failed" : result.status,
|
|
37
|
+
errorCode: result.errorCode,
|
|
38
|
+
errorMessage: result.errorMessage,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function createExecBackedSession(options: ExecBackedSessionOptions): AgentSession {
|
|
43
|
+
return new ExecBackedSession(options);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
class ExecBackedSession implements AgentSession {
|
|
47
|
+
private _state: SessionState = "idle";
|
|
48
|
+
private params: Record<string, unknown> | null;
|
|
49
|
+
private active: AbortController | null = null;
|
|
50
|
+
private inFlight: Promise<TurnResult> | null = null;
|
|
51
|
+
private draining = false;
|
|
52
|
+
private readonly goals: GoalController;
|
|
53
|
+
|
|
54
|
+
constructor(private readonly options: ExecBackedSessionOptions) {
|
|
55
|
+
this.params = options.sessionCodec.deserialize(options.ctx.sessionParams ?? null);
|
|
56
|
+
this.goals = new GoalController({
|
|
57
|
+
providerType: options.providerType,
|
|
58
|
+
capability: options.capabilities?.goals ?? EMULATED_GOAL_CAPABILITY,
|
|
59
|
+
getSessionId: () => this.sessionId,
|
|
60
|
+
send: (message) => this.send(message),
|
|
61
|
+
dispatch: (event) => void Promise.resolve(options.ctx.onEvent?.(event)).catch(() => undefined),
|
|
62
|
+
});
|
|
63
|
+
if (options.ctx.signal) {
|
|
64
|
+
if (options.ctx.signal.aborted) void this.close();
|
|
65
|
+
else options.ctx.signal.addEventListener("abort", () => void this.close(), { once: true });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get sessionId(): string | null {
|
|
70
|
+
return this.options.sessionCodec.getDisplayId?.(this.params) ?? null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get state(): SessionState {
|
|
74
|
+
return this._state;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async send(message: string, sendOptions?: SendOptions): Promise<SendHandle> {
|
|
78
|
+
if (this._state === "closed") throw new Error("Session is closed");
|
|
79
|
+
if (this.draining) throw new Error("Session is draining");
|
|
80
|
+
if (this.inFlight) throw new Error(`${this.options.providerType} session is busy`);
|
|
81
|
+
const uuid = uuidv7();
|
|
82
|
+
const result = this.run(message, sendOptions);
|
|
83
|
+
this.inFlight = result;
|
|
84
|
+
void result.then((settled) => this.goals.onTurnSettled(settled)).catch(() => undefined);
|
|
85
|
+
return { uuid, result };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private async run(message: string, sendOptions?: SendOptions): Promise<TurnResult> {
|
|
89
|
+
this._state = "thinking";
|
|
90
|
+
const controller = new AbortController();
|
|
91
|
+
this.active = controller;
|
|
92
|
+
const onAbort = () => controller.abort();
|
|
93
|
+
sendOptions?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
94
|
+
try {
|
|
95
|
+
const result = await this.options.execute({
|
|
96
|
+
prompt: message,
|
|
97
|
+
cwd: this.options.ctx.cwd,
|
|
98
|
+
env: this.options.ctx.env,
|
|
99
|
+
config: {
|
|
100
|
+
...this.options.ctx.config,
|
|
101
|
+
...(sendOptions?.timeoutSec !== undefined ? { timeoutSec: sendOptions.timeoutSec } : {}),
|
|
102
|
+
},
|
|
103
|
+
sessionParams: this.params,
|
|
104
|
+
signal: controller.signal,
|
|
105
|
+
onOutput: this.options.ctx.onOutput,
|
|
106
|
+
onLifecycle: (event) => this.options.ctx.onLifecycle?.(event),
|
|
107
|
+
onEvent: async (event: StreamEvent) => {
|
|
108
|
+
if (event.sessionId) {
|
|
109
|
+
this.params = this.options.sessionCodec.serialize({
|
|
110
|
+
sessionId: event.sessionId,
|
|
111
|
+
...(this.options.ctx.cwd ? { cwd: this.options.ctx.cwd } : {}),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (event.type === "tool_call") this._state = "tool_executing";
|
|
115
|
+
else if (event.type === "assistant" || event.type === "thinking") this._state = "thinking";
|
|
116
|
+
await this.options.ctx.onEvent?.(event);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
if (result.clearSession) this.params = null;
|
|
120
|
+
if (result.sessionParams) this.params = this.options.sessionCodec.serialize(result.sessionParams);
|
|
121
|
+
return turnResult(result);
|
|
122
|
+
} finally {
|
|
123
|
+
sendOptions?.signal?.removeEventListener("abort", onAbort);
|
|
124
|
+
this.active = null;
|
|
125
|
+
this.inFlight = null;
|
|
126
|
+
this.restoreIdle();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private restoreIdle(): void {
|
|
131
|
+
if (this._state !== "closed") this._state = "idle";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async cancel(_uuid: string): Promise<CancelResult> {
|
|
135
|
+
return { cancelled: false };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async stopTask(_taskId: string): Promise<StopTaskResult> {
|
|
139
|
+
return { stopped: false };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
setGoal(objective: string, goalOptions?: GoalOptions): Promise<SetGoalResult> {
|
|
143
|
+
return this.goals.setGoal(objective, goalOptions);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
clearGoal(clearOptions?: { reason?: "cleared" | "blocked" }): Promise<ClearGoalResult> {
|
|
147
|
+
return this.goals.clearGoal(clearOptions);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
getGoal(): GoalState | null {
|
|
151
|
+
return this.goals.getGoal();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async interrupt(): Promise<void> {
|
|
155
|
+
this.active?.abort();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async drain(): Promise<void> {
|
|
159
|
+
this.draining = true;
|
|
160
|
+
if (this.inFlight) await this.inFlight.catch(() => undefined);
|
|
161
|
+
await this.close();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async close(): Promise<void> {
|
|
165
|
+
this._state = "closed";
|
|
166
|
+
this.active?.abort();
|
|
167
|
+
if (this.inFlight) await this.inFlight.catch(() => undefined);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HistoryAttachment,
|
|
3
|
+
HistoryCatchUpOptions,
|
|
4
|
+
HistoryCheckpoint,
|
|
5
|
+
SessionAttachment,
|
|
6
|
+
} from "../types.js";
|
|
7
|
+
|
|
8
|
+
interface FileCheckpointValue {
|
|
9
|
+
offset: number;
|
|
10
|
+
eventId?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function decodeCheckpoint(
|
|
14
|
+
checkpoint: HistoryCheckpoint | undefined,
|
|
15
|
+
kind: string,
|
|
16
|
+
): FileCheckpointValue | undefined {
|
|
17
|
+
if (!checkpoint) return undefined;
|
|
18
|
+
if (checkpoint.kind !== kind) {
|
|
19
|
+
throw new Error(`history checkpoint kind must be ${kind}`);
|
|
20
|
+
}
|
|
21
|
+
const value = checkpoint.value;
|
|
22
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
23
|
+
throw new Error("history checkpoint value must be an object");
|
|
24
|
+
}
|
|
25
|
+
const offset = (value as Record<string, unknown>)["offset"];
|
|
26
|
+
const eventId = (value as Record<string, unknown>)["eventId"];
|
|
27
|
+
if (typeof offset !== "number" || !Number.isSafeInteger(offset) || offset < 0) {
|
|
28
|
+
throw new Error("history checkpoint offset must be a non-negative safe integer");
|
|
29
|
+
}
|
|
30
|
+
if (eventId !== undefined && typeof eventId !== "string") {
|
|
31
|
+
throw new Error("history checkpoint eventId must be a string");
|
|
32
|
+
}
|
|
33
|
+
return { offset, ...(eventId ? { eventId } : {}) };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Adapt the legacy byte-offset transcript contract to the additive history API. */
|
|
37
|
+
export function historyFromSessionAttachment(
|
|
38
|
+
providerType: string,
|
|
39
|
+
attachment: SessionAttachment,
|
|
40
|
+
): HistoryAttachment {
|
|
41
|
+
const checkpointKind = `${providerType}:byte-offset:v1`;
|
|
42
|
+
return {
|
|
43
|
+
record: attachment.record,
|
|
44
|
+
historySource: attachment.transcript
|
|
45
|
+
? { kind: "file", path: attachment.transcript.filePath }
|
|
46
|
+
: null,
|
|
47
|
+
lastTurn: attachment.lastTurn,
|
|
48
|
+
catchUp(options?: HistoryCatchUpOptions) {
|
|
49
|
+
if (options?.mode === "bounded_full_resync") {
|
|
50
|
+
options = { ...options, after: undefined };
|
|
51
|
+
}
|
|
52
|
+
const decoded = decodeCheckpoint(options?.after, checkpointKind);
|
|
53
|
+
return {
|
|
54
|
+
async *[Symbol.asyncIterator]() {
|
|
55
|
+
for await (const yielded of attachment.catchUp({
|
|
56
|
+
...(decoded ? { fromOffset: decoded.offset } : {}),
|
|
57
|
+
...(decoded?.eventId ? { sinceEventId: decoded.eventId } : {}),
|
|
58
|
+
})) {
|
|
59
|
+
yield {
|
|
60
|
+
event: yielded.event,
|
|
61
|
+
eventId: yielded.eventId,
|
|
62
|
+
checkpoint: {
|
|
63
|
+
kind: checkpointKind,
|
|
64
|
+
value: {
|
|
65
|
+
offset: yielded.offset,
|
|
66
|
+
...(yielded.eventId ? { eventId: yielded.eventId } : {}),
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
resume: (ctx) => attachment.resume(ctx),
|
|
75
|
+
};
|
|
76
|
+
}
|
package/src/sessions/index.ts
CHANGED
|
@@ -6,3 +6,6 @@ export {
|
|
|
6
6
|
assertSessionRecord,
|
|
7
7
|
} from "./record.js";
|
|
8
8
|
export type { CreateSessionRecordInput } from "./record.js";
|
|
9
|
+
export { historyFromSessionAttachment } from "./history.js";
|
|
10
|
+
export { createExecBackedSession } from "./exec-backed.js";
|
|
11
|
+
export type { ExecBackedSessionOptions } from "./exec-backed.js";
|
package/src/types.ts
CHANGED
|
@@ -93,6 +93,51 @@ export interface ProviderCapabilities {
|
|
|
93
93
|
* Codex); absent elsewhere. See internal-docs/spec-durable-sessions.md.
|
|
94
94
|
*/
|
|
95
95
|
durableSessions?: boolean;
|
|
96
|
+
/** Provider-neutral durable history through `attachHistory`. */
|
|
97
|
+
durableHistory?: boolean;
|
|
98
|
+
/** Read-only discovery and import of locally persisted sessions. */
|
|
99
|
+
localHistory?: boolean;
|
|
100
|
+
/** A persisted provider session can be resumed. */
|
|
101
|
+
resume?: boolean;
|
|
102
|
+
/** Models expose provider-native variants independently from effort. */
|
|
103
|
+
modelVariants?: boolean;
|
|
104
|
+
/** The provider can surface interactive permission requests. */
|
|
105
|
+
permissionRequests?: boolean;
|
|
106
|
+
/** The provider can surface structured questions. */
|
|
107
|
+
questionRequests?: boolean;
|
|
108
|
+
/** Ambient MCP configuration can be excluded by a proved mechanism. */
|
|
109
|
+
strictMcpIsolation?: boolean;
|
|
110
|
+
/** Connected upstream-provider credentials can be removed safely. */
|
|
111
|
+
upstreamProviderDisconnect?: boolean;
|
|
112
|
+
/** Selection changes can be applied without starting a new host chat. */
|
|
113
|
+
sessionModelChange?: boolean;
|
|
114
|
+
sessionVariantChange?: boolean;
|
|
115
|
+
sessionEffortChange?: boolean;
|
|
116
|
+
sessionModeChange?: boolean;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type CapabilityStatus = "supported" | "missing" | "upgrade_required" | "degraded";
|
|
120
|
+
|
|
121
|
+
export interface ProviderRuntimeContext {
|
|
122
|
+
cwd?: string;
|
|
123
|
+
env?: Record<string, string>;
|
|
124
|
+
config?: ProviderConfig;
|
|
125
|
+
refresh?: boolean;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ProviderRuntimeReport {
|
|
129
|
+
binary: {
|
|
130
|
+
status: CapabilityStatus;
|
|
131
|
+
command: string | null;
|
|
132
|
+
version: string | null;
|
|
133
|
+
protocolProfile: string | null;
|
|
134
|
+
reason?: string;
|
|
135
|
+
};
|
|
136
|
+
capabilities: Partial<Record<keyof ProviderCapabilities, {
|
|
137
|
+
supported: boolean;
|
|
138
|
+
status: CapabilityStatus;
|
|
139
|
+
reason?: string;
|
|
140
|
+
}>>;
|
|
96
141
|
}
|
|
97
142
|
|
|
98
143
|
// ---------------------------------------------------------------------------
|
|
@@ -121,7 +166,7 @@ export interface ProviderCapabilities {
|
|
|
121
166
|
export type GoalStatus = "active" | "paused" | "met" | "blocked" | "cleared";
|
|
122
167
|
|
|
123
168
|
/** Why a `blocked` goal is blocked. */
|
|
124
|
-
export type GoalBlockedReason = "budget" | "needs_input" | "max_iterations";
|
|
169
|
+
export type GoalBlockedReason = "budget" | "needs_input" | "max_iterations" | "sentinel_error";
|
|
125
170
|
|
|
126
171
|
/** Who last changed the goal state. */
|
|
127
172
|
export type GoalSource = "host" | "model" | "sentinel" | "agentex";
|
|
@@ -165,6 +210,8 @@ export interface GoalState {
|
|
|
165
210
|
source: GoalSource;
|
|
166
211
|
/** Why a `blocked` goal is blocked. Absent unless status === "blocked". */
|
|
167
212
|
blockedReason?: GoalBlockedReason;
|
|
213
|
+
/** Error from goal evaluation when blockedReason is `sentinel_error`. */
|
|
214
|
+
errorMessage?: string;
|
|
168
215
|
/** Codex telemetry, when the provider reports it (reverse-engineered fields). */
|
|
169
216
|
tokensUsed?: number;
|
|
170
217
|
timeUsedSeconds?: number;
|
|
@@ -286,8 +333,12 @@ export interface ProviderModule {
|
|
|
286
333
|
*/
|
|
287
334
|
resolveAuth(ctx?: AuthResolveContext): Promise<AuthReport>;
|
|
288
335
|
sessionCodec?: SessionCodec;
|
|
289
|
-
/** List available models
|
|
290
|
-
listModels?(options?:
|
|
336
|
+
/** List available models in a runtime context. */
|
|
337
|
+
listModels?(options?: ListModelsOptions): Promise<ProviderModel[]>;
|
|
338
|
+
/** Probe the selected binary and wire protocol for effective capabilities. */
|
|
339
|
+
probeCapabilities?(ctx?: ProviderRuntimeContext): Promise<ProviderRuntimeReport>;
|
|
340
|
+
/** Optional management surface for harness-owned upstream providers. */
|
|
341
|
+
upstreamProviders?: UpstreamProviderManager;
|
|
291
342
|
/**
|
|
292
343
|
* List the operating modes this provider exposes (see `AgentMode`). Present
|
|
293
344
|
* only on providers with `capabilities.modes === true`. May spawn the agent
|
|
@@ -317,6 +368,10 @@ export interface ProviderModule {
|
|
|
317
368
|
* never auto-invoked). See internal-docs/spec-durable-sessions.md.
|
|
318
369
|
*/
|
|
319
370
|
attachSession?(record: SessionRecord, opts?: AttachOptions): Promise<SessionAttachment>;
|
|
371
|
+
/** Additive file-or-service durable history surface. */
|
|
372
|
+
attachHistory?(record: SessionRecord, opts?: AttachOptions): Promise<HistoryAttachment>;
|
|
373
|
+
/** Discover and read local sessions when their ids are not already known. */
|
|
374
|
+
localHistory?: import("./history/types.js").LocalHistoryOps;
|
|
320
375
|
}
|
|
321
376
|
|
|
322
377
|
// Result of looking up a transcript for a given session.
|
|
@@ -448,7 +503,7 @@ export interface CatchUpYield {
|
|
|
448
503
|
* only bites Claude tool-call turns.)
|
|
449
504
|
*/
|
|
450
505
|
offset: number;
|
|
451
|
-
/** Stable
|
|
506
|
+
/** Stable replay id. Claude uses its wire id and file-backed Codex uses a deterministic synthetic id. */
|
|
452
507
|
eventId: string | null;
|
|
453
508
|
}
|
|
454
509
|
|
|
@@ -462,6 +517,8 @@ export interface CatchUpOptions {
|
|
|
462
517
|
export interface AttachOptions {
|
|
463
518
|
/** Env overlay for home-dir resolution (same vars the transcript helpers honor). */
|
|
464
519
|
env?: Record<string, string>;
|
|
520
|
+
/** Runtime selection for service-backed attachment. */
|
|
521
|
+
config?: ProviderConfig;
|
|
465
522
|
}
|
|
466
523
|
|
|
467
524
|
/** Read-only reattachment to a session's durable state. See `attachSession`. */
|
|
@@ -484,6 +541,36 @@ export interface SessionAttachment {
|
|
|
484
541
|
resume(ctx?: SessionContext): Promise<AgentSession>;
|
|
485
542
|
}
|
|
486
543
|
|
|
544
|
+
/** Opaque, provider-owned checkpoint for file or service history. */
|
|
545
|
+
export interface HistoryCheckpoint {
|
|
546
|
+
kind: string;
|
|
547
|
+
value: unknown;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export type HistorySource =
|
|
551
|
+
| { kind: "file"; path: string }
|
|
552
|
+
| { kind: "service"; description: string };
|
|
553
|
+
|
|
554
|
+
export interface HistoryCatchUpYield {
|
|
555
|
+
event: StreamEvent;
|
|
556
|
+
checkpoint: HistoryCheckpoint;
|
|
557
|
+
eventId: string | null;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
export interface HistoryCatchUpOptions {
|
|
561
|
+
after?: HistoryCheckpoint;
|
|
562
|
+
mode?: "incremental" | "bounded_full_resync";
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
export interface HistoryAttachment {
|
|
566
|
+
record: SessionRecord;
|
|
567
|
+
historySource: HistorySource | null;
|
|
568
|
+
lastTurn: LastTurnStatus;
|
|
569
|
+
catchUp(opts?: HistoryCatchUpOptions): AsyncIterable<HistoryCatchUpYield>;
|
|
570
|
+
resume(ctx?: SessionContext): Promise<AgentSession>;
|
|
571
|
+
close?(): Promise<void>;
|
|
572
|
+
}
|
|
573
|
+
|
|
487
574
|
// Execution input
|
|
488
575
|
export interface ExecutionContext {
|
|
489
576
|
prompt: string;
|
|
@@ -567,9 +654,15 @@ export interface ProviderEndpointConfig {
|
|
|
567
654
|
export interface ProviderConfig {
|
|
568
655
|
command?: string;
|
|
569
656
|
model?: string;
|
|
657
|
+
/** Provider-native model variant, independent from reasoning effort. */
|
|
658
|
+
modelVariant?: string;
|
|
570
659
|
/** Provider reasoning effort. Claude maps this to `--effort`; Codex sends
|
|
571
660
|
* it as the app-server `turn/start.effort` override. */
|
|
572
661
|
effort?: string;
|
|
662
|
+
/** Behavior when a host does not provide or loses its input callback. */
|
|
663
|
+
unattendedPermissionPolicy?: "allow" | "deny";
|
|
664
|
+
/** Deadline for a host to answer an observed permission or question. Default 300s. */
|
|
665
|
+
inputRequestTimeoutSec?: number;
|
|
573
666
|
maxTurns?: number;
|
|
574
667
|
/**
|
|
575
668
|
* Hard runtime cap, in seconds.
|
|
@@ -1068,6 +1161,7 @@ export type StreamEvent =
|
|
|
1068
1161
|
enforced: boolean;
|
|
1069
1162
|
source: GoalSource;
|
|
1070
1163
|
blockedReason?: GoalBlockedReason;
|
|
1164
|
+
errorMessage?: string;
|
|
1071
1165
|
tokensUsed?: number;
|
|
1072
1166
|
timeUsedSeconds?: number;
|
|
1073
1167
|
tokenBudget?: number;
|
|
@@ -1229,6 +1323,69 @@ export interface ProviderModel {
|
|
|
1229
1323
|
id: string;
|
|
1230
1324
|
name: string;
|
|
1231
1325
|
provider?: string;
|
|
1326
|
+
providerName?: string;
|
|
1327
|
+
variants?: Array<{
|
|
1328
|
+
id: string;
|
|
1329
|
+
name: string;
|
|
1330
|
+
description?: string;
|
|
1331
|
+
isDefault?: boolean;
|
|
1332
|
+
disabled?: boolean;
|
|
1333
|
+
}>;
|
|
1334
|
+
supportedEfforts?: string[];
|
|
1335
|
+
defaultEffort?: string;
|
|
1336
|
+
contextWindow?: number;
|
|
1337
|
+
maxOutputTokens?: number;
|
|
1338
|
+
inputCostPerMillion?: number;
|
|
1339
|
+
outputCostPerMillion?: number;
|
|
1340
|
+
supportsImages?: boolean;
|
|
1341
|
+
supportsTools?: boolean;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
export interface ListModelsOptions extends ProviderRuntimeContext {
|
|
1345
|
+
cacheTtlMs?: number;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
export interface UpstreamProvider {
|
|
1349
|
+
id: string;
|
|
1350
|
+
name: string;
|
|
1351
|
+
connected: boolean;
|
|
1352
|
+
authMethodIds: string[];
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
export interface ProviderAuthMethod {
|
|
1356
|
+
id: string;
|
|
1357
|
+
name: string;
|
|
1358
|
+
type: "api_key" | "oauth";
|
|
1359
|
+
prompts?: Array<{
|
|
1360
|
+
id: string;
|
|
1361
|
+
label: string;
|
|
1362
|
+
type: "text" | "select";
|
|
1363
|
+
options?: Array<{ value: string; label: string }>;
|
|
1364
|
+
}>;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
export interface ProviderAuthFlow {
|
|
1368
|
+
id: string;
|
|
1369
|
+
providerId: string;
|
|
1370
|
+
url: string | null;
|
|
1371
|
+
completion: "code" | "automatic";
|
|
1372
|
+
instructions: string | null;
|
|
1373
|
+
expiresAt: string;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
export interface UpstreamProviderManager {
|
|
1377
|
+
list(ctx?: ProviderRuntimeContext): Promise<UpstreamProvider[]>;
|
|
1378
|
+
authMethods(providerId: string, ctx?: ProviderRuntimeContext): Promise<ProviderAuthMethod[]>;
|
|
1379
|
+
setApiKey(providerId: string, key: string, ctx?: ProviderRuntimeContext): Promise<void>;
|
|
1380
|
+
beginOAuth(
|
|
1381
|
+
providerId: string,
|
|
1382
|
+
methodId: string,
|
|
1383
|
+
inputs?: Record<string, string>,
|
|
1384
|
+
ctx?: ProviderRuntimeContext,
|
|
1385
|
+
): Promise<ProviderAuthFlow>;
|
|
1386
|
+
completeOAuth(flowId: string, code?: string, ctx?: ProviderRuntimeContext): Promise<void>;
|
|
1387
|
+
canDisconnect(providerId: string, ctx?: ProviderRuntimeContext): Promise<boolean>;
|
|
1388
|
+
disconnect(providerId: string, ctx?: ProviderRuntimeContext): Promise<void>;
|
|
1232
1389
|
}
|
|
1233
1390
|
|
|
1234
1391
|
// MCP server configuration
|
|
@@ -1496,6 +1653,8 @@ export interface AgentSession {
|
|
|
1496
1653
|
* `capabilities.durableSessions === true`.
|
|
1497
1654
|
*/
|
|
1498
1655
|
describe?(): SessionRecord | null;
|
|
1656
|
+
/** Additive provider-neutral durable-history identity. */
|
|
1657
|
+
describeHistory?(): SessionRecord | null;
|
|
1499
1658
|
}
|
|
1500
1659
|
|
|
1501
1660
|
/** Result of a single turn within a session. */
|
package/src/utils/auth.ts
CHANGED
|
@@ -89,7 +89,7 @@ export function detectAuth(providerType: string, env: Record<string, string>): R
|
|
|
89
89
|
return { method: "subscription", billingType: "subscription" };
|
|
90
90
|
}
|
|
91
91
|
case "cursor": {
|
|
92
|
-
if (hasEnv(env, "CURSOR_API_KEY")
|
|
92
|
+
if (hasEnv(env, "CURSOR_API_KEY")) {
|
|
93
93
|
return { method: "api_key", billingType: "api" };
|
|
94
94
|
}
|
|
95
95
|
return { method: "subscription", billingType: "subscription" };
|
|
@@ -435,21 +435,50 @@ async function resolveGeminiAuth(ctx?: AuthResolveContext): Promise<AuthReport>
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
// ---------------------------------------------------------------------------
|
|
438
|
-
// Cursor
|
|
439
|
-
//
|
|
438
|
+
// Cursor uses its selected binary's `status` command. Other providers in this
|
|
439
|
+
// section use environment or native-store presence reports.
|
|
440
440
|
// ---------------------------------------------------------------------------
|
|
441
441
|
|
|
442
442
|
async function resolveCursorAuth(ctx?: AuthResolveContext): Promise<AuthReport> {
|
|
443
443
|
const env = buildEnv(ctx?.env);
|
|
444
444
|
const binary = await checkBinary("agent", ctx?.command, env);
|
|
445
|
+
const apiKey = {
|
|
446
|
+
method: "api_key" as const,
|
|
447
|
+
source: { kind: "env" as const, var: "CURSOR_API_KEY" },
|
|
448
|
+
present: hasEnv(env, "CURSOR_API_KEY"),
|
|
449
|
+
};
|
|
450
|
+
let nativePresent = false;
|
|
451
|
+
let usedCli = false;
|
|
452
|
+
if (binary.installed && binary.resolvedPath) {
|
|
453
|
+
try {
|
|
454
|
+
const result = await runChildProcess({
|
|
455
|
+
runId: "cursor-auth-status",
|
|
456
|
+
command: binary.resolvedPath,
|
|
457
|
+
args: [...binary.prefixArgs, "status"],
|
|
458
|
+
cwd: process.cwd(),
|
|
459
|
+
env,
|
|
460
|
+
timeoutSec: 10,
|
|
461
|
+
});
|
|
462
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
463
|
+
nativePresent = result.exitCode === 0 && /logged\s+in|authenticated|account\s*:/i.test(output)
|
|
464
|
+
&& !/not\s+logged\s+in|unauthenticated/i.test(output);
|
|
465
|
+
usedCli = true;
|
|
466
|
+
} catch {
|
|
467
|
+
// Presence remains unknown and false for an unsupported old binary.
|
|
468
|
+
}
|
|
469
|
+
}
|
|
445
470
|
return {
|
|
446
471
|
providerType: "cursor",
|
|
447
472
|
binary: stripPrefixArgs(binary),
|
|
448
473
|
options: [
|
|
449
|
-
|
|
450
|
-
{
|
|
474
|
+
apiKey,
|
|
475
|
+
{
|
|
476
|
+
method: "subscription",
|
|
477
|
+
source: { kind: "cli", command: "agent status" },
|
|
478
|
+
present: nativePresent,
|
|
479
|
+
},
|
|
451
480
|
],
|
|
452
|
-
source: "filesystem",
|
|
481
|
+
source: usedCli ? "cli" : "filesystem",
|
|
453
482
|
};
|
|
454
483
|
}
|
|
455
484
|
|
package/src/utils/binary.ts
CHANGED
|
@@ -28,8 +28,11 @@ const COMMON_PATHS_UNIX: Record<string, string[]> = {
|
|
|
28
28
|
],
|
|
29
29
|
agent: [
|
|
30
30
|
path.join(os.homedir(), ".local", "bin", "agent"),
|
|
31
|
+
path.join(os.homedir(), ".local", "bin", "cursor-agent"),
|
|
31
32
|
"/usr/local/bin/agent",
|
|
33
|
+
"/usr/local/bin/cursor-agent",
|
|
32
34
|
path.join(os.homedir(), ".npm-global", "bin", "agent"),
|
|
35
|
+
path.join(os.homedir(), ".npm-global", "bin", "cursor-agent"),
|
|
33
36
|
],
|
|
34
37
|
opencode: [
|
|
35
38
|
path.join(os.homedir(), ".local", "bin", "opencode"),
|
package/src/utils/endpoint.ts
CHANGED
|
@@ -158,13 +158,13 @@ function translateCodexEndpoint(e: ProviderEndpointConfig): EndpointTranslation
|
|
|
158
158
|
// name), never argv: argv is world-readable via `ps` and a header can carry
|
|
159
159
|
// a secret (Authorization, X-API-Key). Mirrors why the claude provider
|
|
160
160
|
// stages MCP headers to a 0600 file instead of the command line. Header
|
|
161
|
-
// names are
|
|
162
|
-
//
|
|
161
|
+
// names are emitted as quoted TOML path segments because valid HTTP token
|
|
162
|
+
// characters such as `.` must not become nested configuration paths.
|
|
163
163
|
let i = 0;
|
|
164
164
|
for (const [name, value] of Object.entries(e.headers)) {
|
|
165
165
|
const headerEnv = `${CODEX_CUSTOM_HEADER_ENV_PREFIX}${i++}`;
|
|
166
166
|
env[headerEnv] = value;
|
|
167
|
-
set(`model_providers.${id}.env_http_headers.${name}`, headerEnv);
|
|
167
|
+
set(`model_providers.${id}.env_http_headers.${JSON.stringify(name)}`, headerEnv);
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
// modelMap intentionally ignored — Codex has no tier aliases.
|