@shivaedev/agent-codex 0.1.0 → 0.3.0
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 +12 -0
- package/README.md +47 -1
- package/dist/adapters/Request.d.ts +4 -0
- package/dist/adapters/Request.d.ts.map +1 -0
- package/dist/adapters/Request.js +2 -0
- package/dist/adapters/Request.js.map +1 -0
- package/dist/adapters/UserMessageItem.d.ts +22 -0
- package/dist/adapters/UserMessageItem.d.ts.map +1 -0
- package/dist/adapters/UserMessageItem.js +24 -0
- package/dist/adapters/UserMessageItem.js.map +1 -0
- package/dist/adapters/attributed.d.ts +6 -0
- package/dist/adapters/attributed.d.ts.map +1 -0
- package/dist/adapters/attributed.js +17 -0
- package/dist/adapters/attributed.js.map +1 -0
- package/dist/adapters/backend.d.ts +5 -0
- package/dist/adapters/backend.d.ts.map +1 -0
- package/dist/adapters/backend.js +23 -0
- package/dist/adapters/backend.js.map +1 -0
- package/dist/adapters/census.d.ts +4 -0
- package/dist/adapters/census.d.ts.map +1 -0
- package/dist/adapters/census.js +10 -0
- package/dist/adapters/census.js.map +1 -0
- package/dist/adapters/codexFailure.d.ts +3 -0
- package/dist/adapters/codexFailure.d.ts.map +1 -0
- package/dist/adapters/codexFailure.js +5 -0
- package/dist/adapters/codexFailure.js.map +1 -0
- package/dist/adapters/configuredBackend.d.ts +6 -0
- package/dist/adapters/configuredBackend.d.ts.map +1 -0
- package/dist/adapters/configuredBackend.js +3 -0
- package/dist/adapters/configuredBackend.js.map +1 -0
- package/dist/adapters/connect.d.ts +11 -0
- package/dist/adapters/connect.d.ts.map +1 -0
- package/dist/adapters/connect.js +100 -0
- package/dist/adapters/connect.js.map +1 -0
- package/dist/adapters/delivery.d.ts +18 -0
- package/dist/adapters/delivery.d.ts.map +1 -0
- package/dist/adapters/delivery.js +115 -0
- package/dist/adapters/delivery.js.map +1 -0
- package/dist/adapters/deliveryState.d.ts +31 -0
- package/dist/adapters/deliveryState.d.ts.map +1 -0
- package/dist/adapters/deliveryState.js +29 -0
- package/dist/adapters/deliveryState.js.map +1 -0
- package/dist/adapters/engine.d.ts +8 -0
- package/dist/adapters/engine.d.ts.map +1 -0
- package/dist/adapters/engine.js +9 -0
- package/dist/adapters/engine.js.map +1 -0
- package/dist/adapters/features.d.ts +726 -0
- package/dist/adapters/features.d.ts.map +1 -0
- package/dist/adapters/features.js +46 -0
- package/dist/adapters/features.js.map +1 -0
- package/dist/adapters/handshake.d.ts +5 -0
- package/dist/adapters/handshake.d.ts.map +1 -0
- package/dist/adapters/handshake.js +30 -0
- package/dist/adapters/handshake.js.map +1 -0
- package/dist/adapters/initializeCodex.d.ts +770 -0
- package/dist/adapters/initializeCodex.d.ts.map +1 -0
- package/dist/adapters/initializeCodex.js +8 -0
- package/dist/adapters/initializeCodex.js.map +1 -0
- package/dist/adapters/initializeEngine.d.ts +774 -0
- package/dist/adapters/initializeEngine.d.ts.map +1 -0
- package/dist/adapters/initializeEngine.js +63 -0
- package/dist/adapters/initializeEngine.js.map +1 -0
- package/dist/adapters/items.d.ts +7 -0
- package/dist/adapters/items.d.ts.map +1 -0
- package/dist/adapters/items.js +105 -0
- package/dist/adapters/items.js.map +1 -0
- package/dist/adapters/mapping.d.ts +9 -0
- package/dist/adapters/mapping.d.ts.map +1 -0
- package/dist/adapters/mapping.js +57 -0
- package/dist/adapters/mapping.js.map +1 -0
- package/dist/adapters/process.d.ts +15 -0
- package/dist/adapters/process.d.ts.map +1 -0
- package/dist/adapters/process.js +55 -0
- package/dist/adapters/process.js.map +1 -0
- package/dist/adapters/protocol.d.ts +178 -0
- package/dist/adapters/protocol.d.ts.map +1 -0
- package/dist/adapters/protocol.js +97 -0
- package/dist/adapters/protocol.js.map +1 -0
- package/dist/adapters/protocolItems.d.ts +89 -0
- package/dist/adapters/protocolItems.d.ts.map +1 -0
- package/dist/adapters/protocolItems.js +100 -0
- package/dist/adapters/protocolItems.js.map +1 -0
- package/dist/adapters/rateLimits.d.ts +7 -0
- package/dist/adapters/rateLimits.d.ts.map +1 -0
- package/dist/adapters/rateLimits.js +40 -0
- package/dist/adapters/rateLimits.js.map +1 -0
- package/dist/adapters/reads.d.ts +324 -0
- package/dist/adapters/reads.d.ts.map +1 -0
- package/dist/adapters/reads.js +83 -0
- package/dist/adapters/reads.js.map +1 -0
- package/dist/adapters/recovery.d.ts +324 -0
- package/dist/adapters/recovery.d.ts.map +1 -0
- package/dist/adapters/recovery.js +39 -0
- package/dist/adapters/recovery.js.map +1 -0
- package/dist/adapters/residualApproval.d.ts +4 -0
- package/dist/adapters/residualApproval.d.ts.map +1 -0
- package/dist/adapters/residualApproval.js +21 -0
- package/dist/adapters/residualApproval.js.map +1 -0
- package/dist/adapters/rpc.d.ts +8 -0
- package/dist/adapters/rpc.d.ts.map +1 -0
- package/dist/adapters/rpc.js +2 -0
- package/dist/adapters/rpc.js.map +1 -0
- package/dist/adapters/rpcProtocol.d.ts +11 -0
- package/dist/adapters/rpcProtocol.d.ts.map +1 -0
- package/dist/adapters/rpcProtocol.js +29 -0
- package/dist/adapters/rpcProtocol.js.map +1 -0
- package/dist/adapters/server.d.ts +30 -0
- package/dist/adapters/server.d.ts.map +1 -0
- package/dist/adapters/server.js +68 -0
- package/dist/adapters/server.js.map +1 -0
- package/dist/adapters/session.d.ts +312 -0
- package/dist/adapters/session.d.ts.map +1 -0
- package/dist/adapters/session.js +45 -0
- package/dist/adapters/session.js.map +1 -0
- package/dist/adapters/subagentItems.d.ts +16 -0
- package/dist/adapters/subagentItems.d.ts.map +1 -0
- package/dist/adapters/subagentItems.js +58 -0
- package/dist/adapters/subagentItems.js.map +1 -0
- package/dist/adapters/tellCliVersionOnce.d.ts +5 -0
- package/dist/adapters/tellCliVersionOnce.d.ts.map +1 -0
- package/dist/adapters/tellCliVersionOnce.js +13 -0
- package/dist/adapters/tellCliVersionOnce.js.map +1 -0
- package/dist/adapters/threadClaims.d.ts +8 -0
- package/dist/adapters/threadClaims.d.ts.map +1 -0
- package/dist/adapters/threadClaims.js +20 -0
- package/dist/adapters/threadClaims.js.map +1 -0
- package/dist/adapters/threadSweep.d.ts +15 -0
- package/dist/adapters/threadSweep.d.ts.map +1 -0
- package/dist/adapters/threadSweep.js +57 -0
- package/dist/adapters/threadSweep.js.map +1 -0
- package/dist/adapters/threadTree.d.ts +8 -0
- package/dist/adapters/threadTree.d.ts.map +1 -0
- package/dist/adapters/threadTree.js +125 -0
- package/dist/adapters/threadTree.js.map +1 -0
- package/dist/adapters/turnRequests.d.ts +13 -0
- package/dist/adapters/turnRequests.d.ts.map +1 -0
- package/dist/adapters/turnRequests.js +27 -0
- package/dist/adapters/turnRequests.js.map +1 -0
- package/dist/adapters/userMessageEvent.d.ts +7 -0
- package/dist/adapters/userMessageEvent.d.ts.map +1 -0
- package/dist/adapters/userMessageEvent.js +19 -0
- package/dist/adapters/userMessageEvent.js.map +1 -0
- package/dist/codex.d.ts +10 -1
- package/dist/codex.d.ts.map +1 -1
- package/dist/codex.js +15 -1
- package/dist/codex.js.map +1 -1
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -0
- package/package.json +23 -6
- package/src/adapters/Request.ts +3 -0
- package/src/adapters/UserMessageItem.ts +28 -0
- package/src/adapters/attributed.ts +21 -0
- package/src/adapters/backend.ts +29 -0
- package/src/adapters/census.ts +17 -0
- package/src/adapters/codexFailure.ts +4 -0
- package/src/adapters/configuredBackend.ts +6 -0
- package/src/adapters/connect.ts +129 -0
- package/src/adapters/delivery.ts +137 -0
- package/src/adapters/deliveryState.ts +43 -0
- package/src/adapters/engine.ts +11 -0
- package/src/adapters/features.ts +62 -0
- package/src/adapters/handshake.ts +35 -0
- package/src/adapters/initializeCodex.ts +8 -0
- package/src/adapters/initializeEngine.ts +75 -0
- package/src/adapters/items.ts +133 -0
- package/src/adapters/mapping.ts +66 -0
- package/src/adapters/process.ts +69 -0
- package/src/adapters/protocol.ts +125 -0
- package/src/adapters/protocolItems.ts +113 -0
- package/src/adapters/rateLimits.ts +55 -0
- package/src/adapters/reads.ts +93 -0
- package/src/adapters/recovery.ts +44 -0
- package/src/adapters/residualApproval.ts +23 -0
- package/src/adapters/rpc.ts +8 -0
- package/src/adapters/rpcProtocol.ts +28 -0
- package/src/adapters/server.ts +96 -0
- package/src/adapters/session.ts +71 -0
- package/src/adapters/subagentItems.ts +73 -0
- package/src/adapters/tellCliVersionOnce.ts +20 -0
- package/src/adapters/threadClaims.ts +26 -0
- package/src/adapters/threadSweep.ts +79 -0
- package/src/adapters/threadTree.ts +149 -0
- package/src/adapters/turnRequests.ts +45 -0
- package/src/adapters/userMessageEvent.ts +22 -0
- package/src/codex.ts +15 -1
- package/src/config.ts +17 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Option, Schema } from "effect";
|
|
2
|
+
import type { RunEvent } from "@shivaedev/agent/events.ts";
|
|
3
|
+
import { itemCompleted, itemStarted } from "#adapters/items.ts";
|
|
4
|
+
import {
|
|
5
|
+
ItemNotification,
|
|
6
|
+
ModelReroutedNotification,
|
|
7
|
+
ThreadStatusNotification,
|
|
8
|
+
TokenUsageNotification,
|
|
9
|
+
TurnNotification,
|
|
10
|
+
} from "#adapters/protocol.ts";
|
|
11
|
+
import { RATE_LIMITS_METHOD, rateLimitEvents } from "#adapters/rateLimits.ts";
|
|
12
|
+
import type { RpcNotification } from "#adapters/rpc.ts";
|
|
13
|
+
export function rawOf(kind: string, payload: unknown) {
|
|
14
|
+
return { kind, payload: JSON.stringify(payload), source: "codex" };
|
|
15
|
+
}
|
|
16
|
+
function mapped<A, E>(schema: Schema.Codec<A, E>, notification: RpcNotification, project: (value: A) => RunEvent[]): RunEvent[] {
|
|
17
|
+
return Option.match(Schema.decodeUnknownOption(schema)(notification.params), {
|
|
18
|
+
onNone: () => [{ raw: rawOf(notification.method, notification.params), type: "raw" }],
|
|
19
|
+
onSome: project,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
export function toAgentEvents(notification: RpcNotification, model: string): RunEvent[] {
|
|
23
|
+
const { method, params } = notification;
|
|
24
|
+
const raw = rawOf(method, params);
|
|
25
|
+
switch (method) {
|
|
26
|
+
case RATE_LIMITS_METHOD:
|
|
27
|
+
return rateLimitEvents(raw, params);
|
|
28
|
+
case "item/started":
|
|
29
|
+
return mapped(ItemNotification, notification, ({ item }) => itemStarted(raw, item));
|
|
30
|
+
case "item/completed":
|
|
31
|
+
return mapped(ItemNotification, notification, ({ item }) => itemCompleted(raw, item));
|
|
32
|
+
case "turn/started":
|
|
33
|
+
return mapped(TurnNotification, notification, () => [{ raw, state: "running", type: "execution" }]);
|
|
34
|
+
case "turn/completed":
|
|
35
|
+
return mapped(TurnNotification, notification, ({ turn }) => [
|
|
36
|
+
{ raw, state: turn.status === "inProgress" ? "running" : turn.status, type: "execution" },
|
|
37
|
+
]);
|
|
38
|
+
case "thread/status/changed":
|
|
39
|
+
return mapped(ThreadStatusNotification, notification, ({ status }) =>
|
|
40
|
+
status.type === "active"
|
|
41
|
+
? [{ raw, state: status.activeFlags.length > 0 ? "awaiting-input" : "running", type: "execution" }]
|
|
42
|
+
: [{ raw, type: "raw" }],
|
|
43
|
+
);
|
|
44
|
+
case "thread/tokenUsage/updated":
|
|
45
|
+
return mapped(TokenUsageNotification, notification, ({ tokenUsage }) => {
|
|
46
|
+
const tokens = tokenUsage.total;
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
cacheReadTokens: tokens.cachedInputTokens,
|
|
50
|
+
inputTokens: tokens.inputTokens,
|
|
51
|
+
outputTokens: tokens.outputTokens,
|
|
52
|
+
scope: "cumulative",
|
|
53
|
+
type: "usage",
|
|
54
|
+
unit: "session",
|
|
55
|
+
...(model.length === 0 ? {} : { model }),
|
|
56
|
+
...(tokens.cacheWriteInputTokens === undefined ? {} : { cacheWriteTokens: tokens.cacheWriteInputTokens }),
|
|
57
|
+
raw,
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
});
|
|
61
|
+
case "model/rerouted":
|
|
62
|
+
return mapped(ModelReroutedNotification, notification, ({ toModel, reason }) => [{ model: toModel, raw, reason, type: "model.changed" }]);
|
|
63
|
+
default:
|
|
64
|
+
return [{ raw, type: "raw" }];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
export interface LineProcess {
|
|
4
|
+
readonly kill: () => void;
|
|
5
|
+
readonly onExit: (listener: (code: number | null) => void) => void;
|
|
6
|
+
readonly onLine: (listener: (line: string) => void) => void;
|
|
7
|
+
readonly onStderr: (listener: (text: string) => void) => void;
|
|
8
|
+
readonly write: (line: string) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface SpawnLineProcessOptions {
|
|
12
|
+
readonly args: readonly string[];
|
|
13
|
+
readonly command: string;
|
|
14
|
+
readonly cwd: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Codex app-server uses newline-delimited JSON over stdio.
|
|
18
|
+
export function spawnLineProcess(options: SpawnLineProcessOptions): LineProcess {
|
|
19
|
+
const child = spawn(options.command, [...options.args], {
|
|
20
|
+
cwd: options.cwd,
|
|
21
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
22
|
+
});
|
|
23
|
+
let lineListener: ((line: string) => void) | null = null;
|
|
24
|
+
let stderrListener: ((text: string) => void) | null = null;
|
|
25
|
+
const exitListeners: ((code: number | null) => void)[] = [];
|
|
26
|
+
function emitExit(code: number | null): void {
|
|
27
|
+
for (const listener of exitListeners) {
|
|
28
|
+
listener(code);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
let buffer = "";
|
|
32
|
+
child.stdout.setEncoding("utf8");
|
|
33
|
+
child.stdout.on("data", (chunk: string) => {
|
|
34
|
+
buffer += chunk;
|
|
35
|
+
let newline = buffer.indexOf("\n");
|
|
36
|
+
while (newline >= 0) {
|
|
37
|
+
const line = buffer.slice(0, newline).trim();
|
|
38
|
+
buffer = buffer.slice(newline + 1);
|
|
39
|
+
if (line.length > 0) {
|
|
40
|
+
lineListener?.(line);
|
|
41
|
+
}
|
|
42
|
+
newline = buffer.indexOf("\n");
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
child.stderr.setEncoding("utf8");
|
|
46
|
+
child.stderr.on("data", (chunk: string) => stderrListener?.(chunk));
|
|
47
|
+
child.on("exit", (code) => emitExit(code));
|
|
48
|
+
child.on("error", () => emitExit(null));
|
|
49
|
+
child.stdin.on("error", () => emitExit(null));
|
|
50
|
+
return {
|
|
51
|
+
// Closing stdin lets app-server exit cleanly; SIGTERM handles a child that remains alive.
|
|
52
|
+
kill: () => {
|
|
53
|
+
child.stdin.end();
|
|
54
|
+
child.kill("SIGTERM");
|
|
55
|
+
},
|
|
56
|
+
onExit: (listener) => {
|
|
57
|
+
exitListeners.push(listener);
|
|
58
|
+
},
|
|
59
|
+
onLine: (listener) => {
|
|
60
|
+
lineListener = listener;
|
|
61
|
+
},
|
|
62
|
+
onStderr: (listener) => {
|
|
63
|
+
stderrListener = listener;
|
|
64
|
+
},
|
|
65
|
+
write: (line) => {
|
|
66
|
+
child.stdin.write(`${line}\n`);
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
|
|
3
|
+
export const PINNED_CLI_VERSION = "0.154.0-alpha.6.2";
|
|
4
|
+
|
|
5
|
+
export const InitializeResponse = Schema.Struct({ userAgent: Schema.String });
|
|
6
|
+
|
|
7
|
+
const ThreadRef = Schema.Struct({ id: Schema.String });
|
|
8
|
+
|
|
9
|
+
export const ThreadResponse = Schema.Struct({ thread: ThreadRef });
|
|
10
|
+
|
|
11
|
+
const ReasoningEffort = Schema.NonEmptyString;
|
|
12
|
+
|
|
13
|
+
export const TurnStatus = Schema.Literals(["completed", "interrupted", "failed", "inProgress"]);
|
|
14
|
+
|
|
15
|
+
export const TurnError = Schema.Struct({ message: Schema.String });
|
|
16
|
+
|
|
17
|
+
export const Turn = Schema.Struct({
|
|
18
|
+
durationMs: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
19
|
+
error: Schema.optional(Schema.NullOr(TurnError)),
|
|
20
|
+
id: Schema.String,
|
|
21
|
+
status: TurnStatus,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const TurnResponse = Schema.Struct({ turn: Turn });
|
|
25
|
+
|
|
26
|
+
export const ModelListResponse = Schema.Struct({
|
|
27
|
+
data: Schema.Array(
|
|
28
|
+
Schema.Struct({
|
|
29
|
+
defaultReasoningEffort: ReasoningEffort,
|
|
30
|
+
displayName: Schema.String,
|
|
31
|
+
isDefault: Schema.Boolean,
|
|
32
|
+
model: Schema.String,
|
|
33
|
+
supportedReasoningEfforts: Schema.Array(Schema.Struct({ reasoningEffort: ReasoningEffort })),
|
|
34
|
+
}),
|
|
35
|
+
),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
export const DynamicToolCallParams = Schema.Struct({
|
|
39
|
+
arguments: Schema.Unknown,
|
|
40
|
+
callId: Schema.String,
|
|
41
|
+
threadId: Schema.String,
|
|
42
|
+
tool: Schema.String,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export const ItemNotification = Schema.Struct({
|
|
46
|
+
item: Schema.Unknown,
|
|
47
|
+
threadId: Schema.String,
|
|
48
|
+
turnId: Schema.String,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
export const TurnNotification = Schema.Struct({
|
|
52
|
+
threadId: Schema.String,
|
|
53
|
+
turn: Turn,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const TokenBreakdown = Schema.Struct({
|
|
57
|
+
cachedInputTokens: Schema.Number,
|
|
58
|
+
cacheWriteInputTokens: Schema.optional(Schema.Number),
|
|
59
|
+
inputTokens: Schema.Number,
|
|
60
|
+
outputTokens: Schema.Number,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const TokenUsageNotification = Schema.Struct({
|
|
64
|
+
threadId: Schema.String,
|
|
65
|
+
tokenUsage: Schema.Struct({ last: TokenBreakdown, total: TokenBreakdown }),
|
|
66
|
+
turnId: Schema.String,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export const ModelReroutedNotification = Schema.Struct({
|
|
70
|
+
fromModel: Schema.String,
|
|
71
|
+
reason: Schema.String,
|
|
72
|
+
threadId: Schema.String,
|
|
73
|
+
toModel: Schema.String,
|
|
74
|
+
turnId: Schema.String,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
export const ThreadSettingsUpdatedNotification = Schema.Struct({
|
|
78
|
+
threadId: Schema.String,
|
|
79
|
+
threadSettings: Schema.Struct({ model: Schema.String }),
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
export const ThreadScoped = Schema.Struct({ threadId: Schema.String });
|
|
83
|
+
|
|
84
|
+
const SpawnSource = Schema.Struct({
|
|
85
|
+
subAgent: Schema.Struct({
|
|
86
|
+
threadSpawn: Schema.Struct({
|
|
87
|
+
agentNickname: Schema.optional(Schema.NullOr(Schema.String)),
|
|
88
|
+
agentPath: Schema.optional(Schema.NullOr(Schema.String)),
|
|
89
|
+
agentRole: Schema.optional(Schema.NullOr(Schema.String)),
|
|
90
|
+
parentThreadId: Schema.String,
|
|
91
|
+
}).pipe(
|
|
92
|
+
Schema.encodeKeys({ agentNickname: "agent_nickname", agentPath: "agent_path", agentRole: "agent_role", parentThreadId: "parent_thread_id" }),
|
|
93
|
+
),
|
|
94
|
+
}).pipe(Schema.encodeKeys({ threadSpawn: "thread_spawn" })),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export const SpawnedThread = Schema.Struct({
|
|
98
|
+
thread: Schema.Struct({ id: Schema.String, source: SpawnSource }),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export const ThreadStatus = Schema.Union([
|
|
102
|
+
Schema.Struct({
|
|
103
|
+
activeFlags: Schema.Array(Schema.String),
|
|
104
|
+
type: Schema.Literal("active"),
|
|
105
|
+
}),
|
|
106
|
+
Schema.Struct({ type: Schema.Literal("idle") }),
|
|
107
|
+
Schema.Struct({ type: Schema.Literal("notLoaded") }),
|
|
108
|
+
Schema.Struct({ type: Schema.Literal("systemError") }),
|
|
109
|
+
]);
|
|
110
|
+
|
|
111
|
+
export const ThreadListResponse = Schema.Struct({
|
|
112
|
+
data: Schema.Array(
|
|
113
|
+
Schema.Struct({
|
|
114
|
+
id: Schema.String,
|
|
115
|
+
source: SpawnSource,
|
|
116
|
+
status: ThreadStatus,
|
|
117
|
+
}),
|
|
118
|
+
),
|
|
119
|
+
nextCursor: Schema.optional(Schema.NullOr(Schema.String)),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
export const ThreadStatusNotification = Schema.Struct({
|
|
123
|
+
status: ThreadStatus,
|
|
124
|
+
threadId: Schema.String,
|
|
125
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { TurnError } from "#adapters/protocol.ts";
|
|
3
|
+
import { UserMessageItem } from "#adapters/UserMessageItem.ts";
|
|
4
|
+
|
|
5
|
+
const item = { id: Schema.String };
|
|
6
|
+
|
|
7
|
+
export const ExecutionStatus = Schema.Literals(["inProgress", "completed", "failed", "declined"]);
|
|
8
|
+
|
|
9
|
+
const AgentMessageItem = Schema.Struct({
|
|
10
|
+
...item,
|
|
11
|
+
text: Schema.String,
|
|
12
|
+
type: Schema.Literal("agentMessage"),
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
const ReasoningItem = Schema.Struct({
|
|
16
|
+
...item,
|
|
17
|
+
content: Schema.optional(Schema.Array(Schema.String)),
|
|
18
|
+
summary: Schema.optional(Schema.Array(Schema.String)),
|
|
19
|
+
type: Schema.Literal("reasoning"),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const CommandExecutionItem = Schema.Struct({
|
|
23
|
+
...item,
|
|
24
|
+
aggregatedOutput: Schema.optional(Schema.NullOr(Schema.String)),
|
|
25
|
+
command: Schema.String,
|
|
26
|
+
cwd: Schema.String,
|
|
27
|
+
exitCode: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
28
|
+
status: ExecutionStatus,
|
|
29
|
+
type: Schema.Literal("commandExecution"),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const FileChangeItem = Schema.Struct({
|
|
33
|
+
...item,
|
|
34
|
+
changes: Schema.Array(Schema.Struct({ diff: Schema.String, path: Schema.String })),
|
|
35
|
+
status: ExecutionStatus,
|
|
36
|
+
type: Schema.Literal("fileChange"),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const McpToolCallItem = Schema.Struct({
|
|
40
|
+
...item,
|
|
41
|
+
arguments: Schema.Unknown,
|
|
42
|
+
error: Schema.optional(Schema.NullOr(TurnError)),
|
|
43
|
+
result: Schema.optional(Schema.Unknown),
|
|
44
|
+
server: Schema.String,
|
|
45
|
+
status: Schema.Literals(["inProgress", "completed", "failed"]),
|
|
46
|
+
tool: Schema.String,
|
|
47
|
+
type: Schema.Literal("mcpToolCall"),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const ContentPart = Schema.Struct({
|
|
51
|
+
text: Schema.optional(Schema.String),
|
|
52
|
+
type: Schema.String,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Codex reports dynamic-tool outcomes through `success` in addition to status.
|
|
56
|
+
const DynamicToolCallItem = Schema.Struct({
|
|
57
|
+
...item,
|
|
58
|
+
arguments: Schema.Unknown,
|
|
59
|
+
contentItems: Schema.optional(Schema.NullOr(Schema.Array(ContentPart))),
|
|
60
|
+
status: Schema.Literals(["inProgress", "completed", "failed"]),
|
|
61
|
+
success: Schema.optional(Schema.NullOr(Schema.Boolean)),
|
|
62
|
+
tool: Schema.String,
|
|
63
|
+
type: Schema.Literal("dynamicToolCall"),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const WebSearchItem = Schema.Struct({
|
|
67
|
+
...item,
|
|
68
|
+
query: Schema.String,
|
|
69
|
+
type: Schema.Literal("webSearch"),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const SubAgentActivityItem = Schema.Struct({
|
|
73
|
+
...item,
|
|
74
|
+
agentPath: Schema.String,
|
|
75
|
+
agentThreadId: Schema.String,
|
|
76
|
+
kind: Schema.Literals(["started", "interacted", "interrupted", "completed"]),
|
|
77
|
+
type: Schema.Literal("subAgentActivity"),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Codex reports collaboration operations as dynamic-tool items.
|
|
81
|
+
const CollabAgentToolCallItem = Schema.Struct({
|
|
82
|
+
...item,
|
|
83
|
+
prompt: Schema.optional(Schema.NullOr(Schema.String)),
|
|
84
|
+
receiverThreadIds: Schema.Array(Schema.String),
|
|
85
|
+
senderThreadId: Schema.String,
|
|
86
|
+
status: Schema.Literals(["inProgress", "completed", "failed", "interrupted"]),
|
|
87
|
+
tool: Schema.Literals([
|
|
88
|
+
"spawnAgent",
|
|
89
|
+
"sendInput",
|
|
90
|
+
"resumeAgent",
|
|
91
|
+
"wait",
|
|
92
|
+
"closeAgent",
|
|
93
|
+
"sendMessage",
|
|
94
|
+
"followupTask",
|
|
95
|
+
"interruptAgent",
|
|
96
|
+
"listAgents",
|
|
97
|
+
]),
|
|
98
|
+
type: Schema.Literal("collabAgentToolCall"),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
export const KnownItem = Schema.Union([
|
|
102
|
+
AgentMessageItem,
|
|
103
|
+
UserMessageItem,
|
|
104
|
+
ReasoningItem,
|
|
105
|
+
CollabAgentToolCallItem,
|
|
106
|
+
CommandExecutionItem,
|
|
107
|
+
DynamicToolCallItem,
|
|
108
|
+
FileChangeItem,
|
|
109
|
+
McpToolCallItem,
|
|
110
|
+
SubAgentActivityItem,
|
|
111
|
+
WebSearchItem,
|
|
112
|
+
]);
|
|
113
|
+
export type KnownItem = typeof KnownItem.Type;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { Raw as RawSchema } from "@shivaedev/agent/eventData.ts";
|
|
2
|
+
import type { RunEvent as AgentEvent } from "@shivaedev/agent/events.ts";
|
|
3
|
+
|
|
4
|
+
type RawPayload = typeof RawSchema.Type;
|
|
5
|
+
|
|
6
|
+
import { Option, Schema } from "effect";
|
|
7
|
+
|
|
8
|
+
export const RATE_LIMITS_METHOD = "account/rateLimits/updated";
|
|
9
|
+
|
|
10
|
+
const CodexWindow = Schema.Struct({
|
|
11
|
+
resetsAt: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
12
|
+
usedPercent: Schema.Number,
|
|
13
|
+
windowDurationMins: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const RateLimitsNotification = Schema.Struct({
|
|
17
|
+
rateLimits: Schema.Struct({
|
|
18
|
+
primary: Schema.optional(Schema.NullOr(CodexWindow)),
|
|
19
|
+
rateLimitReachedType: Schema.optional(Schema.NullOr(Schema.String)),
|
|
20
|
+
secondary: Schema.optional(Schema.NullOr(CodexWindow)),
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
type Snapshot = typeof RateLimitsNotification.Type.rateLimits;
|
|
25
|
+
type Window = Extract<AgentEvent, { type: "rate.limit" }>["windows"][number];
|
|
26
|
+
|
|
27
|
+
const decodeRateLimits = Schema.decodeUnknownOption(RateLimitsNotification);
|
|
28
|
+
|
|
29
|
+
function present<T>(value: T | null | undefined): value is T {
|
|
30
|
+
return value !== null && value !== undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function windowOf(window: typeof CodexWindow.Type): Window {
|
|
34
|
+
return {
|
|
35
|
+
...(present(window.windowDurationMins) ? { durationMinutes: window.windowDurationMins } : {}),
|
|
36
|
+
...(present(window.resetsAt) ? { resetsAt: window.resetsAt * 1000 } : {}),
|
|
37
|
+
usedPercent: window.usedPercent,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function event(raw: RawPayload, snapshot: Snapshot): AgentEvent {
|
|
42
|
+
return {
|
|
43
|
+
raw,
|
|
44
|
+
status: present(snapshot.rateLimitReachedType) ? "rejected" : "unknown",
|
|
45
|
+
type: "rate.limit",
|
|
46
|
+
windows: [snapshot.primary, snapshot.secondary].filter(present).map(windowOf),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function rateLimitEvents(raw: RawPayload, params: unknown): AgentEvent[] {
|
|
51
|
+
return Option.match(decodeRateLimits(params), {
|
|
52
|
+
onNone: () => [{ raw, type: "raw" }],
|
|
53
|
+
onSome: ({ rateLimits }) => [event(raw, rateLimits)],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import type { RunEvent } from "@shivaedev/agent/events.ts";
|
|
3
|
+
import { ProtocolFailure, type ReadFailure } from "@shivaedev/agent/failures.ts";
|
|
4
|
+
import type { Observation, Receipt } from "@shivaedev/agent/run.ts";
|
|
5
|
+
import { itemCompleted, itemStarted } from "#adapters/items.ts";
|
|
6
|
+
import { rawOf } from "#adapters/mapping.ts";
|
|
7
|
+
import { AppServer } from "#adapters/server.ts";
|
|
8
|
+
export const Item = Schema.Struct({ clientId: Schema.optional(Schema.NullOr(Schema.String)), id: Schema.String, type: Schema.String });
|
|
9
|
+
export const Turn = Schema.Struct({
|
|
10
|
+
id: Schema.String,
|
|
11
|
+
items: Schema.Array(Schema.Unknown),
|
|
12
|
+
status: Schema.Literals(["inProgress", "completed", "failed", "interrupted"]),
|
|
13
|
+
});
|
|
14
|
+
const Turns = Schema.Struct({ data: Schema.Array(Turn), nextCursor: Schema.NullOr(Schema.String) });
|
|
15
|
+
const Items = Schema.Struct({ data: Schema.Array(Schema.Struct({ item: Schema.Unknown })), nextCursor: Schema.NullOr(Schema.String) });
|
|
16
|
+
export function decode<A, E>(schema: Schema.Codec<A, E>, value: unknown) {
|
|
17
|
+
return Schema.decodeUnknownEffect(schema)(value).pipe(
|
|
18
|
+
Effect.mapError(() => new ProtocolFailure({ backend: "codex", message: "Incompatible Codex response" })),
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
export const readRequest = Effect.fn("Codex.readRequest")(function* (method: string, params: unknown) {
|
|
22
|
+
const server = yield* AppServer;
|
|
23
|
+
return yield* server.request(method, params).pipe(
|
|
24
|
+
Effect.mapError(
|
|
25
|
+
(error): ReadFailure =>
|
|
26
|
+
error._tag === "BackendUnavailable" || error._tag === "ProtocolFailure"
|
|
27
|
+
? error
|
|
28
|
+
: new ProtocolFailure({
|
|
29
|
+
backend: "codex",
|
|
30
|
+
message: error.message,
|
|
31
|
+
...(error.providerCode === undefined ? {} : { providerCode: error.providerCode }),
|
|
32
|
+
}),
|
|
33
|
+
),
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
export const turns = Effect.fn("Codex.turns")(function* (threadId: string) {
|
|
37
|
+
const found: (typeof Turn.Type)[] = [];
|
|
38
|
+
const seen = new Set<string>();
|
|
39
|
+
let cursor: string | null = null;
|
|
40
|
+
do {
|
|
41
|
+
const page: typeof Turns.Type = yield* readRequest("thread/turns/list", {
|
|
42
|
+
cursor,
|
|
43
|
+
itemsView: "full",
|
|
44
|
+
limit: 100,
|
|
45
|
+
sortDirection: "desc",
|
|
46
|
+
threadId,
|
|
47
|
+
}).pipe(Effect.flatMap((value) => decode(Turns, value)));
|
|
48
|
+
found.push(...page.data);
|
|
49
|
+
cursor = page.nextCursor;
|
|
50
|
+
if (cursor !== null && seen.has(cursor)) {
|
|
51
|
+
return yield* new ProtocolFailure({ backend: "codex", message: "Repeated turn cursor" });
|
|
52
|
+
}
|
|
53
|
+
if (cursor !== null) {
|
|
54
|
+
seen.add(cursor);
|
|
55
|
+
}
|
|
56
|
+
} while (cursor !== null);
|
|
57
|
+
return found;
|
|
58
|
+
});
|
|
59
|
+
export function observe(receipt: Receipt) {
|
|
60
|
+
return turns(receipt.session?.value ?? "").pipe(
|
|
61
|
+
Effect.map((values): Observation => {
|
|
62
|
+
const turn = values.find((candidate) => candidate.id === receipt.reference.value);
|
|
63
|
+
return turn === undefined
|
|
64
|
+
? { reason: "The accepted turn is not observable; absence does not authorize replay", type: "missing" }
|
|
65
|
+
: { state: turn.status === "inProgress" ? "running" : turn.status, type: "known" };
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
export const transcript = Effect.fn("Codex.transcript")(function* (receipt: Receipt) {
|
|
70
|
+
const events: RunEvent[] = [];
|
|
71
|
+
const seen = new Set<string>();
|
|
72
|
+
let cursor: string | null = null;
|
|
73
|
+
do {
|
|
74
|
+
const page: typeof Items.Type = yield* readRequest("thread/items/list", {
|
|
75
|
+
cursor,
|
|
76
|
+
limit: 100,
|
|
77
|
+
sortDirection: "asc",
|
|
78
|
+
threadId: receipt.session?.value,
|
|
79
|
+
turnId: receipt.reference.value,
|
|
80
|
+
}).pipe(Effect.flatMap((value) => decode(Items, value)));
|
|
81
|
+
for (const { item } of page.data) {
|
|
82
|
+
events.push(...itemStarted(rawOf("thread/items/list", item), item), ...itemCompleted(rawOf("thread/items/list", item), item));
|
|
83
|
+
}
|
|
84
|
+
cursor = page.nextCursor;
|
|
85
|
+
if (cursor !== null && seen.has(cursor)) {
|
|
86
|
+
return yield* new ProtocolFailure({ backend: "codex", message: "Repeated item cursor" });
|
|
87
|
+
}
|
|
88
|
+
if (cursor !== null) {
|
|
89
|
+
seen.add(cursor);
|
|
90
|
+
}
|
|
91
|
+
} while (cursor !== null);
|
|
92
|
+
return { completeness: events.some((event) => event.type === "raw") ? ("partial" as const) : ("complete" as const), events };
|
|
93
|
+
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import type { ReadFailure } from "@shivaedev/agent/failures.ts";
|
|
3
|
+
import type { Receipt, Reconciliation } from "@shivaedev/agent/run.ts";
|
|
4
|
+
import { Item, turns } from "#adapters/reads.ts";
|
|
5
|
+
import { AppServer } from "#adapters/server.ts";
|
|
6
|
+
import type { Session } from "#adapters/session.ts";
|
|
7
|
+
export function receiptOf(operationId: string, threadId: string, turnId: string): Receipt {
|
|
8
|
+
return { backend: "codex", operationId, reference: { value: turnId, version: 1 }, session: { value: threadId, version: 1 } };
|
|
9
|
+
}
|
|
10
|
+
export const recovery = Effect.fn("Codex.recovery")(function* (sessions: Map<string, Session>) {
|
|
11
|
+
const server = yield* AppServer;
|
|
12
|
+
const operations = new Map<string, string | undefined>();
|
|
13
|
+
const refusals = new Map<string, string>();
|
|
14
|
+
const reconcile = Effect.fn("Codex.reconcile")(function* (operationId: string): Effect.fn.Return<Reconciliation, ReadFailure, AppServer> {
|
|
15
|
+
const refusal = refusals.get(operationId);
|
|
16
|
+
if (refusal !== undefined) {
|
|
17
|
+
return { evidence: refusal, operationId, type: "not-accepted" };
|
|
18
|
+
}
|
|
19
|
+
const threadId = operations.get(operationId);
|
|
20
|
+
if (threadId === undefined) {
|
|
21
|
+
return { operationId, reason: "No local thread association; recover the session reference in the application", type: "uncertain" };
|
|
22
|
+
}
|
|
23
|
+
const matches = (yield* turns(threadId)).filter((turn) =>
|
|
24
|
+
turn.items.some((item) => {
|
|
25
|
+
const decoded = Schema.decodeUnknownOption(Item)(item);
|
|
26
|
+
return decoded._tag === "Some" && decoded.value.type === "userMessage" && decoded.value.clientId === operationId;
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
const found = matches[0];
|
|
30
|
+
if (matches.length !== 1 || found === undefined) {
|
|
31
|
+
return {
|
|
32
|
+
operationId,
|
|
33
|
+
reason:
|
|
34
|
+
matches.length > 1
|
|
35
|
+
? "Multiple turns match this operation; preserve ownership"
|
|
36
|
+
: "No matching turn is observable; absence does not authorize replay",
|
|
37
|
+
type: "uncertain",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
yield* sessions.get(threadId)?.driver.recovered(found.id, found.status === "inProgress") ?? Effect.void;
|
|
41
|
+
return { receipt: receiptOf(operationId, threadId, found.id), type: "accepted" };
|
|
42
|
+
});
|
|
43
|
+
return { operations, reconcile: (operationId: string) => reconcile(operationId).pipe(Effect.provideService(AppServer, server)), refusals };
|
|
44
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Option } from "effect";
|
|
2
|
+
import type { RpcServerRequest } from "#adapters/rpc.ts";
|
|
3
|
+
|
|
4
|
+
const DECLINED = "No approval consumer is configured";
|
|
5
|
+
|
|
6
|
+
export function residualApproval(request: RpcServerRequest): Option.Option<unknown> {
|
|
7
|
+
switch (request.method) {
|
|
8
|
+
case "item/commandExecution/requestApproval":
|
|
9
|
+
case "item/fileChange/requestApproval":
|
|
10
|
+
return Option.some({ decision: "decline" });
|
|
11
|
+
case "item/permissions/requestApproval":
|
|
12
|
+
return Option.some({ permissions: {}, scope: "turn" });
|
|
13
|
+
case "item/tool/requestUserInput":
|
|
14
|
+
return Option.some({ answers: {} });
|
|
15
|
+
case "mcpServer/elicitation/request":
|
|
16
|
+
return Option.some({ "_meta": null, action: "decline", content: null });
|
|
17
|
+
case "execCommandApproval":
|
|
18
|
+
case "applyPatchApproval":
|
|
19
|
+
return Option.some({ decision: { denied: { rejection: DECLINED } } });
|
|
20
|
+
default:
|
|
21
|
+
return Option.none();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { ProtocolFailure, Rejected } from "@shivaedev/agent/failures.ts";
|
|
3
|
+
export const Envelope = Schema.Struct({
|
|
4
|
+
error: Schema.optional(Schema.Unknown),
|
|
5
|
+
id: Schema.optional(Schema.Union([Schema.Number, Schema.String])),
|
|
6
|
+
method: Schema.optional(Schema.String),
|
|
7
|
+
params: Schema.optional(Schema.Unknown),
|
|
8
|
+
result: Schema.optional(Schema.Unknown),
|
|
9
|
+
});
|
|
10
|
+
const RpcError = Schema.Struct({
|
|
11
|
+
code: Schema.optional(Schema.Number),
|
|
12
|
+
data: Schema.optional(Schema.Struct({ codexErrorInfo: Schema.optional(Schema.Unknown) })),
|
|
13
|
+
message: Schema.String,
|
|
14
|
+
});
|
|
15
|
+
const Capacity = Schema.Literals(["rateLimitExceeded", "flexUnavailable", "serverOverloaded", "usageLimitExceeded"]);
|
|
16
|
+
export function rejected(value: unknown) {
|
|
17
|
+
const error = Schema.decodeUnknownOption(RpcError)(value);
|
|
18
|
+
if (error._tag === "None") {
|
|
19
|
+
return new ProtocolFailure({ backend: "codex", message: "Invalid Codex rejection" });
|
|
20
|
+
}
|
|
21
|
+
const capacity = Schema.decodeUnknownOption(Capacity)(error.value.data?.codexErrorInfo);
|
|
22
|
+
const code = error.value.code === -32_001 ? "session-queue-full" : String(error.value.code ?? "rejected");
|
|
23
|
+
return new Rejected({
|
|
24
|
+
backend: "codex",
|
|
25
|
+
message: error.value.message,
|
|
26
|
+
providerCode: capacity._tag === "Some" ? capacity.value : code,
|
|
27
|
+
});
|
|
28
|
+
}
|