axexec 1.0.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/LICENSE +21 -0
- package/README.md +196 -0
- package/bin/axexec +17 -0
- package/dist/agents/claude-code/adapter.d.ts +6 -0
- package/dist/agents/claude-code/adapter.js +71 -0
- package/dist/agents/claude-code/parse-event.d.ts +16 -0
- package/dist/agents/claude-code/parse-event.js +185 -0
- package/dist/agents/claude-code/types.d.ts +180 -0
- package/dist/agents/claude-code/types.js +112 -0
- package/dist/agents/codex/adapter.d.ts +6 -0
- package/dist/agents/codex/adapter.js +62 -0
- package/dist/agents/codex/item-parsers.d.ts +17 -0
- package/dist/agents/codex/item-parsers.js +126 -0
- package/dist/agents/codex/parse-event.d.ts +38 -0
- package/dist/agents/codex/parse-event.js +141 -0
- package/dist/agents/codex/types.d.ts +407 -0
- package/dist/agents/codex/types.js +188 -0
- package/dist/agents/copilot/adapter.d.ts +11 -0
- package/dist/agents/copilot/adapter.js +81 -0
- package/dist/agents/copilot/parse-event.d.ts +20 -0
- package/dist/agents/copilot/parse-event.js +137 -0
- package/dist/agents/copilot/stream-session.d.ts +10 -0
- package/dist/agents/copilot/stream-session.js +120 -0
- package/dist/agents/copilot/tail-file.d.ts +11 -0
- package/dist/agents/copilot/tail-file.js +52 -0
- package/dist/agents/copilot/transform-event.d.ts +19 -0
- package/dist/agents/copilot/transform-event.js +100 -0
- package/dist/agents/copilot/types.d.ts +320 -0
- package/dist/agents/copilot/types.js +220 -0
- package/dist/agents/copilot/watch-session.d.ts +26 -0
- package/dist/agents/copilot/watch-session.js +186 -0
- package/dist/agents/gemini/adapter.d.ts +6 -0
- package/dist/agents/gemini/adapter.js +78 -0
- package/dist/agents/gemini/parse-event.d.ts +18 -0
- package/dist/agents/gemini/parse-event.js +144 -0
- package/dist/agents/gemini/types.d.ts +162 -0
- package/dist/agents/gemini/types.js +103 -0
- package/dist/agents/opencode/adapter.d.ts +16 -0
- package/dist/agents/opencode/adapter.js +142 -0
- package/dist/agents/opencode/cleanup-session.d.ts +17 -0
- package/dist/agents/opencode/cleanup-session.js +41 -0
- package/dist/agents/opencode/create-session-start-event.d.ts +18 -0
- package/dist/agents/opencode/create-session-start-event.js +24 -0
- package/dist/agents/opencode/detect-empty-session.d.ts +25 -0
- package/dist/agents/opencode/detect-empty-session.js +42 -0
- package/dist/agents/opencode/map-error-to-event.d.ts +10 -0
- package/dist/agents/opencode/map-error-to-event.js +55 -0
- package/dist/agents/opencode/parse-message-part.d.ts +24 -0
- package/dist/agents/opencode/parse-message-part.js +112 -0
- package/dist/agents/opencode/parse-sse-event.d.ts +23 -0
- package/dist/agents/opencode/parse-sse-event.js +125 -0
- package/dist/agents/opencode/process-sse-events.d.ts +24 -0
- package/dist/agents/opencode/process-sse-events.js +66 -0
- package/dist/agents/opencode/server-types.d.ts +509 -0
- package/dist/agents/opencode/server-types.js +293 -0
- package/dist/agents/opencode/session-api.d.ts +56 -0
- package/dist/agents/opencode/session-api.js +151 -0
- package/dist/agents/opencode/spawn-server.d.ts +29 -0
- package/dist/agents/opencode/spawn-server.js +95 -0
- package/dist/agents/opencode/sse-client.d.ts +33 -0
- package/dist/agents/opencode/sse-client.js +145 -0
- package/dist/agents/registry.d.ts +15 -0
- package/dist/agents/registry.js +24 -0
- package/dist/cli.d.ts +15 -0
- package/dist/cli.js +119 -0
- package/dist/credentials/get-credential-environment.d.ts +13 -0
- package/dist/credentials/get-credential-environment.js +46 -0
- package/dist/credentials/install-credentials.d.ts +27 -0
- package/dist/credentials/install-credentials.js +102 -0
- package/dist/credentials/save-json-file.d.ts +11 -0
- package/dist/credentials/save-json-file.js +21 -0
- package/dist/credentials/types.d.ts +24 -0
- package/dist/credentials/types.js +4 -0
- package/dist/credentials/write-agent-credentials.d.ts +36 -0
- package/dist/credentials/write-agent-credentials.js +91 -0
- package/dist/determine-session-success.d.ts +15 -0
- package/dist/determine-session-success.js +25 -0
- package/dist/format-event-tsv.d.ts +23 -0
- package/dist/format-event-tsv.js +136 -0
- package/dist/parse-credentials.d.ts +13 -0
- package/dist/parse-credentials.js +63 -0
- package/dist/parse-iso-timestamp.d.ts +21 -0
- package/dist/parse-iso-timestamp.js +37 -0
- package/dist/resolve-binary.d.ts +29 -0
- package/dist/resolve-binary.js +46 -0
- package/dist/resolve-output-mode.d.ts +39 -0
- package/dist/resolve-output-mode.js +39 -0
- package/dist/run-agent.d.ts +32 -0
- package/dist/run-agent.js +146 -0
- package/dist/stream-agent.d.ts +20 -0
- package/dist/stream-agent.js +207 -0
- package/dist/types/adapter.d.ts +101 -0
- package/dist/types/adapter.js +14 -0
- package/dist/types/events.d.ts +82 -0
- package/dist/types/events.js +13 -0
- package/dist/types/options.d.ts +41 -0
- package/dist/types/options.js +4 -0
- package/dist/validate-agent.d.ts +20 -0
- package/dist/validate-agent.js +50 -0
- package/dist/write-event.d.ts +23 -0
- package/dist/write-event.js +43 -0
- package/package.json +79 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event parser for Copilot CLI.
|
|
3
|
+
*
|
|
4
|
+
* Parses JSONL events from Copilot CLI session files. The wrapper script
|
|
5
|
+
* (stream-session.ts) tails the session file and outputs events to stdout,
|
|
6
|
+
* which are then parsed by this module.
|
|
7
|
+
*/
|
|
8
|
+
import type { EventParser } from "../../types/adapter.js";
|
|
9
|
+
/**
|
|
10
|
+
* Creates a parser for Copilot CLI JSONL events.
|
|
11
|
+
*
|
|
12
|
+
* Maintains state for:
|
|
13
|
+
* - Tool name correlation (toolCallId -> tool name)
|
|
14
|
+
* - Tool call deduplication (tracks emitted tool.call events)
|
|
15
|
+
* - Model tracking (updated from session.model_change)
|
|
16
|
+
* - Usage accumulation (for session.complete stats)
|
|
17
|
+
* - Turn end tracking (for session.complete emission)
|
|
18
|
+
*/
|
|
19
|
+
declare function createCopilotParser(): EventParser;
|
|
20
|
+
export { createCopilotParser };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event parser for Copilot CLI.
|
|
3
|
+
*
|
|
4
|
+
* Parses JSONL events from Copilot CLI session files. The wrapper script
|
|
5
|
+
* (stream-session.ts) tails the session file and outputs events to stdout,
|
|
6
|
+
* which are then parsed by this module.
|
|
7
|
+
*/
|
|
8
|
+
import { createTimestampParser } from "../../parse-iso-timestamp.js";
|
|
9
|
+
import { CopilotEvent, isAbortEvent, isAssistantMessageEvent, isAssistantTurnEndEvent, isAssistantUsageEvent, isSessionErrorEvent, isSessionModelChangeEvent, isSessionStartEvent, isToolExecutionCompleteEvent, isToolExecutionStartEvent, } from "./types.js";
|
|
10
|
+
import { transformAssistantMessageEvent, transformSessionErrorEvent, transformSessionStartEvent, transformToolCompleteEvent, transformToolStartEvent, } from "./transform-event.js";
|
|
11
|
+
/**
|
|
12
|
+
* Creates a parser for Copilot CLI JSONL events.
|
|
13
|
+
*
|
|
14
|
+
* Maintains state for:
|
|
15
|
+
* - Tool name correlation (toolCallId -> tool name)
|
|
16
|
+
* - Tool call deduplication (tracks emitted tool.call events)
|
|
17
|
+
* - Model tracking (updated from session.model_change)
|
|
18
|
+
* - Usage accumulation (for session.complete stats)
|
|
19
|
+
* - Turn end tracking (for session.complete emission)
|
|
20
|
+
*/
|
|
21
|
+
function createCopilotParser() {
|
|
22
|
+
const parseTimestamp = createTimestampParser();
|
|
23
|
+
const toolNames = new Map();
|
|
24
|
+
const emittedToolCalls = new Set();
|
|
25
|
+
let currentModel = "unknown";
|
|
26
|
+
let accumulatedUsage;
|
|
27
|
+
let sessionStartEmitted = false;
|
|
28
|
+
let lastTurnEndTimestamp;
|
|
29
|
+
let sessionCompleteEmitted = false;
|
|
30
|
+
return (line) => {
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
// Drain call - emit session.complete if we haven't already
|
|
33
|
+
// The adapter signals end-of-stream by calling the parser with an empty string
|
|
34
|
+
if (trimmed === "") {
|
|
35
|
+
return emitSessionComplete();
|
|
36
|
+
}
|
|
37
|
+
// Parse and validate JSON
|
|
38
|
+
const event = parseEventLine(trimmed);
|
|
39
|
+
if (!event) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
// Dispatch based on event type
|
|
43
|
+
return handleEvent(event);
|
|
44
|
+
};
|
|
45
|
+
function emitSessionComplete() {
|
|
46
|
+
if (!sessionCompleteEmitted && lastTurnEndTimestamp !== undefined) {
|
|
47
|
+
sessionCompleteEmitted = true;
|
|
48
|
+
return {
|
|
49
|
+
type: "session.complete",
|
|
50
|
+
stats: {
|
|
51
|
+
durationMs: accumulatedUsage?.durationMs ?? 0,
|
|
52
|
+
costUsd: accumulatedUsage?.costUsd,
|
|
53
|
+
inputTokens: accumulatedUsage?.inputTokens,
|
|
54
|
+
outputTokens: accumulatedUsage?.outputTokens,
|
|
55
|
+
},
|
|
56
|
+
timestamp: lastTurnEndTimestamp,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
function handleEvent(event) {
|
|
62
|
+
if (isSessionStartEvent(event)) {
|
|
63
|
+
if (sessionStartEmitted)
|
|
64
|
+
return undefined;
|
|
65
|
+
sessionStartEmitted = true;
|
|
66
|
+
return transformSessionStartEvent(event, currentModel, parseTimestamp);
|
|
67
|
+
}
|
|
68
|
+
if (isSessionModelChangeEvent(event)) {
|
|
69
|
+
currentModel = event.data.newModel;
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
if (isSessionErrorEvent(event)) {
|
|
73
|
+
return transformSessionErrorEvent(event, parseTimestamp);
|
|
74
|
+
}
|
|
75
|
+
if (isAssistantMessageEvent(event)) {
|
|
76
|
+
return transformAssistantMessageEvent(event, toolNames, parseTimestamp);
|
|
77
|
+
}
|
|
78
|
+
if (isAssistantUsageEvent(event)) {
|
|
79
|
+
accumulateUsage(event);
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
if (isAssistantTurnEndEvent(event)) {
|
|
83
|
+
lastTurnEndTimestamp = parseTimestamp(event.timestamp);
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
if (isToolExecutionStartEvent(event)) {
|
|
87
|
+
return transformToolStartEvent(event, toolNames, emittedToolCalls, parseTimestamp);
|
|
88
|
+
}
|
|
89
|
+
if (isToolExecutionCompleteEvent(event)) {
|
|
90
|
+
return transformToolCompleteEvent(event, toolNames, parseTimestamp);
|
|
91
|
+
}
|
|
92
|
+
if (isAbortEvent(event)) {
|
|
93
|
+
return {
|
|
94
|
+
type: "session.error",
|
|
95
|
+
code: "ABORTED",
|
|
96
|
+
message: event.data.reason,
|
|
97
|
+
timestamp: parseTimestamp(event.timestamp),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
function accumulateUsage(event) {
|
|
103
|
+
if (!accumulatedUsage) {
|
|
104
|
+
accumulatedUsage = {
|
|
105
|
+
inputTokens: 0,
|
|
106
|
+
outputTokens: 0,
|
|
107
|
+
costUsd: 0,
|
|
108
|
+
durationMs: 0,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (event.data.inputTokens) {
|
|
112
|
+
accumulatedUsage.inputTokens += event.data.inputTokens;
|
|
113
|
+
}
|
|
114
|
+
if (event.data.outputTokens) {
|
|
115
|
+
accumulatedUsage.outputTokens += event.data.outputTokens;
|
|
116
|
+
}
|
|
117
|
+
if (event.data.cost) {
|
|
118
|
+
accumulatedUsage.costUsd += event.data.cost;
|
|
119
|
+
}
|
|
120
|
+
if (event.data.duration) {
|
|
121
|
+
accumulatedUsage.durationMs += event.data.duration;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/** Parse a JSONL line into a CopilotEvent */
|
|
126
|
+
function parseEventLine(line) {
|
|
127
|
+
let json;
|
|
128
|
+
try {
|
|
129
|
+
json = JSON.parse(line);
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
const result = CopilotEvent.safeParse(json);
|
|
135
|
+
return result.success ? result.data : undefined;
|
|
136
|
+
}
|
|
137
|
+
export { createCopilotParser };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copilot CLI session file streamer.
|
|
4
|
+
*
|
|
5
|
+
* This script runs Copilot CLI and streams session events from the JSONL
|
|
6
|
+
* session file to stdout. It's designed to be invoked as a wrapper by axexec.
|
|
7
|
+
*
|
|
8
|
+
* Usage: node stream-session.js <prompt> [copilot-args...]
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copilot CLI session file streamer.
|
|
4
|
+
*
|
|
5
|
+
* This script runs Copilot CLI and streams session events from the JSONL
|
|
6
|
+
* session file to stdout. It's designed to be invoked as a wrapper by axexec.
|
|
7
|
+
*
|
|
8
|
+
* Usage: node stream-session.js <prompt> [copilot-args...]
|
|
9
|
+
*/
|
|
10
|
+
import { spawn } from "node:child_process";
|
|
11
|
+
import { tailFile } from "./tail-file.js";
|
|
12
|
+
import { getExistingSessionFiles, waitForSessionFile, } from "./watch-session.js";
|
|
13
|
+
/** Main entry point */
|
|
14
|
+
async function main() {
|
|
15
|
+
const cliArguments = process.argv.slice(2);
|
|
16
|
+
const prompt = cliArguments[0];
|
|
17
|
+
if (!prompt) {
|
|
18
|
+
process.stderr.write("Usage: stream-session.js <prompt> [args...]\n");
|
|
19
|
+
process.exit(2);
|
|
20
|
+
}
|
|
21
|
+
// Build Copilot CLI arguments
|
|
22
|
+
const extraArguments = cliArguments.slice(1);
|
|
23
|
+
const copilotArguments = [
|
|
24
|
+
"-p",
|
|
25
|
+
prompt,
|
|
26
|
+
"--allow-all-tools",
|
|
27
|
+
// Enable network access without confirmation prompts.
|
|
28
|
+
// Copilot requires user approval for URL access by default, but other
|
|
29
|
+
// agents (Gemini, OpenCode) allow it by default. For unified behavior,
|
|
30
|
+
// enable it. This matches how --allow-all-tools works for other operations.
|
|
31
|
+
// See: copilot-cli-decompiled/copilot-source/index.js line 418918
|
|
32
|
+
"--allow-all-urls",
|
|
33
|
+
...extraArguments,
|
|
34
|
+
];
|
|
35
|
+
// Record existing session files
|
|
36
|
+
const existingFiles = getExistingSessionFiles();
|
|
37
|
+
// Abort controller for cleanup
|
|
38
|
+
const abortController = new AbortController();
|
|
39
|
+
// Track resources for cleanup
|
|
40
|
+
let copilotProcess;
|
|
41
|
+
let sessionWatcherCleanup;
|
|
42
|
+
// Cleanup function
|
|
43
|
+
const cleanup = () => {
|
|
44
|
+
abortController.abort();
|
|
45
|
+
sessionWatcherCleanup?.();
|
|
46
|
+
if (copilotProcess && !copilotProcess.killed) {
|
|
47
|
+
copilotProcess.kill();
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
// Handle signals
|
|
51
|
+
process.on("SIGINT", cleanup);
|
|
52
|
+
process.on("SIGTERM", cleanup);
|
|
53
|
+
try {
|
|
54
|
+
// Start Copilot CLI
|
|
55
|
+
const copilotBin = process.env["AXEXEC_COPILOT_PATH"] ?? "copilot";
|
|
56
|
+
const child = spawn(copilotBin, copilotArguments, {
|
|
57
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
58
|
+
});
|
|
59
|
+
copilotProcess = child;
|
|
60
|
+
// Pipe Copilot's stdout/stderr to our stderr (for debugging)
|
|
61
|
+
// We use stdout for JSONL events only
|
|
62
|
+
child.stdout.pipe(process.stderr);
|
|
63
|
+
child.stderr.pipe(process.stderr);
|
|
64
|
+
// Handle spawn errors
|
|
65
|
+
child.on("error", (error) => {
|
|
66
|
+
process.stderr.write(`Error spawning Copilot CLI: ${error.message}\n`);
|
|
67
|
+
cleanup();
|
|
68
|
+
process.exit(1);
|
|
69
|
+
});
|
|
70
|
+
// Wait for new session file
|
|
71
|
+
const sessionWatcher = waitForSessionFile(existingFiles);
|
|
72
|
+
sessionWatcherCleanup = sessionWatcher.cleanup;
|
|
73
|
+
const sessionFilePath = await sessionWatcher.promise;
|
|
74
|
+
sessionWatcherCleanup = undefined;
|
|
75
|
+
// Start tailing the session file
|
|
76
|
+
const tailPromise = (async () => {
|
|
77
|
+
for await (const line of tailFile(sessionFilePath, abortController.signal)) {
|
|
78
|
+
if (line.trim()) {
|
|
79
|
+
process.stdout.write(line + "\n");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
// Wait for Copilot to exit
|
|
84
|
+
const exitCode = await new Promise((resolve) => {
|
|
85
|
+
child.on("close", (code) => {
|
|
86
|
+
resolve(code ?? 0);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
// Give a moment for final events to be written
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
91
|
+
// Stop tailing
|
|
92
|
+
abortController.abort();
|
|
93
|
+
// Wait for tail to finish processing
|
|
94
|
+
try {
|
|
95
|
+
await tailPromise;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Ignore abort errors
|
|
99
|
+
}
|
|
100
|
+
process.exit(exitCode);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
104
|
+
process.stderr.write(`Error: ${message}\n`);
|
|
105
|
+
// Don't call cleanup() here - finally block handles it
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
cleanup();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Run main and handle errors
|
|
113
|
+
try {
|
|
114
|
+
await main();
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
118
|
+
process.stderr.write(`Fatal error: ${message}\n`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File tailing utility for streaming new lines from a file.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Tail a file and yield lines as they're written.
|
|
6
|
+
*
|
|
7
|
+
* Polls the file for new content and yields complete lines as they appear.
|
|
8
|
+
* Buffers partial lines across reads to avoid splitting JSONL records.
|
|
9
|
+
* Continues until the abort signal is triggered.
|
|
10
|
+
*/
|
|
11
|
+
export declare function tailFile(filePath: string, signal: AbortSignal): AsyncGenerator<string>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File tailing utility for streaming new lines from a file.
|
|
3
|
+
*/
|
|
4
|
+
import { createReadStream, statSync } from "node:fs";
|
|
5
|
+
/**
|
|
6
|
+
* Tail a file and yield lines as they're written.
|
|
7
|
+
*
|
|
8
|
+
* Polls the file for new content and yields complete lines as they appear.
|
|
9
|
+
* Buffers partial lines across reads to avoid splitting JSONL records.
|
|
10
|
+
* Continues until the abort signal is triggered.
|
|
11
|
+
*/
|
|
12
|
+
export async function* tailFile(filePath, signal) {
|
|
13
|
+
let position = 0;
|
|
14
|
+
let buffer = "";
|
|
15
|
+
// Keep reading until aborted
|
|
16
|
+
while (!signal.aborted) {
|
|
17
|
+
try {
|
|
18
|
+
const stats = statSync(filePath);
|
|
19
|
+
const fileSize = stats.size;
|
|
20
|
+
if (fileSize > position) {
|
|
21
|
+
// New data available
|
|
22
|
+
const stream = createReadStream(filePath, {
|
|
23
|
+
start: position,
|
|
24
|
+
end: fileSize - 1,
|
|
25
|
+
});
|
|
26
|
+
for await (const chunk of stream) {
|
|
27
|
+
// Check abort during iteration - signal can change while awaiting
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
29
|
+
if (signal.aborted)
|
|
30
|
+
break;
|
|
31
|
+
buffer += chunk.toString("utf8");
|
|
32
|
+
// Yield complete lines (terminated by newline)
|
|
33
|
+
if (buffer.includes("\n")) {
|
|
34
|
+
const lines = buffer.split("\n");
|
|
35
|
+
// Keep last element as buffer (partial line or empty string)
|
|
36
|
+
buffer = lines.pop() ?? "";
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
yield line;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
position = fileSize;
|
|
43
|
+
}
|
|
44
|
+
// Wait a bit before checking again
|
|
45
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// File might be temporarily unavailable, wait and retry
|
|
49
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event transformation functions for Copilot CLI events.
|
|
3
|
+
*
|
|
4
|
+
* Transforms raw Copilot events into normalized AxexecEvent format.
|
|
5
|
+
*/
|
|
6
|
+
import type { AxexecEvent } from "../../types/events.js";
|
|
7
|
+
import type { CopilotAssistantMessageEvent, CopilotSessionErrorEvent, CopilotSessionStartEvent, CopilotToolExecutionCompleteEvent, CopilotToolExecutionStartEvent } from "./types.js";
|
|
8
|
+
type TimestampParser = (isoString: string | undefined) => number;
|
|
9
|
+
/** Transforms a session.start event */
|
|
10
|
+
export declare function transformSessionStartEvent(event: CopilotSessionStartEvent, model: string, parseTimestamp: TimestampParser): AxexecEvent;
|
|
11
|
+
/** Transforms a session.error event */
|
|
12
|
+
export declare function transformSessionErrorEvent(event: CopilotSessionErrorEvent, parseTimestamp: TimestampParser): AxexecEvent;
|
|
13
|
+
/** Transforms an assistant.message event */
|
|
14
|
+
export declare function transformAssistantMessageEvent(event: CopilotAssistantMessageEvent, toolNames: Map<string, string>, parseTimestamp: TimestampParser): AxexecEvent | undefined;
|
|
15
|
+
/** Transforms a tool.execution_start event */
|
|
16
|
+
export declare function transformToolStartEvent(event: CopilotToolExecutionStartEvent, toolNames: Map<string, string>, emittedToolCalls: Set<string>, parseTimestamp: TimestampParser): AxexecEvent | undefined;
|
|
17
|
+
/** Transforms a tool.execution_complete event */
|
|
18
|
+
export declare function transformToolCompleteEvent(event: CopilotToolExecutionCompleteEvent, toolNames: Map<string, string>, parseTimestamp: TimestampParser): AxexecEvent;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event transformation functions for Copilot CLI events.
|
|
3
|
+
*
|
|
4
|
+
* Transforms raw Copilot events into normalized AxexecEvent format.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Infers the provider from a Copilot model name.
|
|
8
|
+
*
|
|
9
|
+
* Copilot supports models from multiple providers:
|
|
10
|
+
* - claude-* → anthropic
|
|
11
|
+
* - gpt-*, o1-*, o3-*, o4-* → openai
|
|
12
|
+
* - gemini-* → google
|
|
13
|
+
*
|
|
14
|
+
* Returns undefined for unrecognized models (field is optional).
|
|
15
|
+
*/
|
|
16
|
+
function inferProviderFromModel(model) {
|
|
17
|
+
if (model.startsWith("claude-"))
|
|
18
|
+
return "anthropic";
|
|
19
|
+
if (model.startsWith("gpt-"))
|
|
20
|
+
return "openai";
|
|
21
|
+
if (model.startsWith("o1-") ||
|
|
22
|
+
model.startsWith("o3-") ||
|
|
23
|
+
model.startsWith("o4-"))
|
|
24
|
+
return "openai";
|
|
25
|
+
if (model.startsWith("gemini-"))
|
|
26
|
+
return "google";
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
/** Transforms a session.start event */
|
|
30
|
+
export function transformSessionStartEvent(event, model, parseTimestamp) {
|
|
31
|
+
const resolvedModel = event.data.selectedModel ?? model;
|
|
32
|
+
return {
|
|
33
|
+
type: "session.start",
|
|
34
|
+
sessionId: event.data.sessionId,
|
|
35
|
+
agent: "copilot",
|
|
36
|
+
model: resolvedModel,
|
|
37
|
+
provider: inferProviderFromModel(resolvedModel),
|
|
38
|
+
timestamp: parseTimestamp(event.timestamp),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Transforms a session.error event */
|
|
42
|
+
export function transformSessionErrorEvent(event, parseTimestamp) {
|
|
43
|
+
return {
|
|
44
|
+
type: "session.error",
|
|
45
|
+
code: event.data.errorType,
|
|
46
|
+
message: event.data.message,
|
|
47
|
+
timestamp: parseTimestamp(event.timestamp),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/** Transforms an assistant.message event */
|
|
51
|
+
export function transformAssistantMessageEvent(event, toolNames, parseTimestamp) {
|
|
52
|
+
const timestamp = parseTimestamp(event.timestamp);
|
|
53
|
+
// If there are tool requests, pre-register the tool names for correlation
|
|
54
|
+
// but DON'T emit tool.call here - we'll emit from tool.execution_start
|
|
55
|
+
for (const request of event.data.toolRequests ?? []) {
|
|
56
|
+
toolNames.set(request.toolCallId, request.name);
|
|
57
|
+
}
|
|
58
|
+
// Emit text content as agent message (only if there's actual content)
|
|
59
|
+
if (event.data.content) {
|
|
60
|
+
return {
|
|
61
|
+
type: "agent.message",
|
|
62
|
+
content: event.data.content,
|
|
63
|
+
timestamp,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
/** Transforms a tool.execution_start event */
|
|
69
|
+
export function transformToolStartEvent(event, toolNames, emittedToolCalls, parseTimestamp) {
|
|
70
|
+
const callId = event.data.toolCallId;
|
|
71
|
+
// Check if we've already emitted this tool call
|
|
72
|
+
if (emittedToolCalls.has(callId)) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
emittedToolCalls.add(callId);
|
|
76
|
+
const tool = event.data.toolName;
|
|
77
|
+
toolNames.set(callId, tool);
|
|
78
|
+
return {
|
|
79
|
+
type: "tool.call",
|
|
80
|
+
callId,
|
|
81
|
+
tool,
|
|
82
|
+
input: event.data.arguments,
|
|
83
|
+
timestamp: parseTimestamp(event.timestamp),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Transforms a tool.execution_complete event */
|
|
87
|
+
export function transformToolCompleteEvent(event, toolNames, parseTimestamp) {
|
|
88
|
+
const callId = event.data.toolCallId;
|
|
89
|
+
const tool = toolNames.get(callId) ?? "unknown";
|
|
90
|
+
// Clean up to prevent memory leaks
|
|
91
|
+
toolNames.delete(callId);
|
|
92
|
+
return {
|
|
93
|
+
type: "tool.result",
|
|
94
|
+
callId,
|
|
95
|
+
tool,
|
|
96
|
+
output: event.data.result?.content ?? event.data.error?.message,
|
|
97
|
+
success: event.data.success,
|
|
98
|
+
timestamp: parseTimestamp(event.timestamp),
|
|
99
|
+
};
|
|
100
|
+
}
|