episoda 0.2.98 → 0.2.100
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.
|
@@ -2786,7 +2786,7 @@ var require_package = __commonJS({
|
|
|
2786
2786
|
"package.json"(exports2, module2) {
|
|
2787
2787
|
module2.exports = {
|
|
2788
2788
|
name: "episoda",
|
|
2789
|
-
version: "0.2.
|
|
2789
|
+
version: "0.2.100",
|
|
2790
2790
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2791
2791
|
main: "dist/index.js",
|
|
2792
2792
|
types: "dist/index.d.ts",
|
|
@@ -8485,8 +8485,10 @@ ${message}`;
|
|
|
8485
8485
|
"--output-format",
|
|
8486
8486
|
"stream-json",
|
|
8487
8487
|
// Structured streaming output
|
|
8488
|
-
"--verbose"
|
|
8488
|
+
"--verbose",
|
|
8489
8489
|
// Required for stream-json with --print
|
|
8490
|
+
"--include-partial-messages"
|
|
8491
|
+
// EP1191: Enable incremental streaming chunks
|
|
8490
8492
|
];
|
|
8491
8493
|
if (session.credentials.preferredModel) {
|
|
8492
8494
|
args.push("--model", session.credentials.preferredModel);
|
|
@@ -8644,14 +8646,25 @@ ${message}`;
|
|
|
8644
8646
|
});
|
|
8645
8647
|
let stdoutBuffer = "";
|
|
8646
8648
|
let extractedSessionId;
|
|
8649
|
+
let stdoutEventCount = 0;
|
|
8650
|
+
let chunksSent = 0;
|
|
8651
|
+
const streamStartTime = Date.now();
|
|
8647
8652
|
childProcess.stdout?.on("data", (data) => {
|
|
8648
|
-
|
|
8653
|
+
const rawData = data.toString();
|
|
8654
|
+
stdoutBuffer += rawData;
|
|
8655
|
+
stdoutEventCount++;
|
|
8656
|
+
if (stdoutEventCount <= 5) {
|
|
8657
|
+
console.log(`[AgentManager] EP1191: stdout event ${stdoutEventCount} - ${rawData.length} chars: ${rawData.substring(0, 200)}${rawData.length > 200 ? "..." : ""}`);
|
|
8658
|
+
}
|
|
8649
8659
|
const lines = stdoutBuffer.split("\n");
|
|
8650
8660
|
stdoutBuffer = lines.pop() || "";
|
|
8651
8661
|
for (const line of lines) {
|
|
8652
8662
|
if (!line.trim()) continue;
|
|
8653
8663
|
try {
|
|
8654
8664
|
const parsed = JSON.parse(line);
|
|
8665
|
+
if (stdoutEventCount <= 10 || chunksSent === 0) {
|
|
8666
|
+
console.log(`[AgentManager] EP1191: Parsed event type: ${parsed.type}`);
|
|
8667
|
+
}
|
|
8655
8668
|
if (provider === "codex") {
|
|
8656
8669
|
switch (parsed.type) {
|
|
8657
8670
|
case "thread.started":
|
|
@@ -8687,6 +8700,10 @@ ${message}`;
|
|
|
8687
8700
|
if (parsed.message?.content) {
|
|
8688
8701
|
for (const block of parsed.message.content) {
|
|
8689
8702
|
if (block.type === "text" && block.text) {
|
|
8703
|
+
chunksSent++;
|
|
8704
|
+
if (chunksSent <= 5) {
|
|
8705
|
+
console.log(`[AgentManager] EP1191: Sending chunk ${chunksSent} via assistant event - ${block.text.length} chars`);
|
|
8706
|
+
}
|
|
8690
8707
|
onChunk(block.text);
|
|
8691
8708
|
}
|
|
8692
8709
|
}
|
|
@@ -8694,6 +8711,10 @@ ${message}`;
|
|
|
8694
8711
|
break;
|
|
8695
8712
|
case "content_block_delta":
|
|
8696
8713
|
if (parsed.delta?.text) {
|
|
8714
|
+
chunksSent++;
|
|
8715
|
+
if (chunksSent <= 5) {
|
|
8716
|
+
console.log(`[AgentManager] EP1191: Sending chunk ${chunksSent} via content_block_delta - ${parsed.delta.text.length} chars`);
|
|
8717
|
+
}
|
|
8697
8718
|
onChunk(parsed.delta.text);
|
|
8698
8719
|
}
|
|
8699
8720
|
break;
|
|
@@ -8735,7 +8756,8 @@ ${message}`;
|
|
|
8735
8756
|
stderrBuffer += data.toString();
|
|
8736
8757
|
});
|
|
8737
8758
|
childProcess.on("exit", (code, signal) => {
|
|
8738
|
-
|
|
8759
|
+
const duration = Date.now() - streamStartTime;
|
|
8760
|
+
console.log(`[AgentManager] EP1191: ${provider} CLI exited for session ${sessionId}: code=${code}, signal=${signal}, duration=${duration}ms, stdoutEvents=${stdoutEventCount}, chunksSent=${chunksSent}`);
|
|
8739
8761
|
this.processes.delete(sessionId);
|
|
8740
8762
|
this.removePidFile(sessionId);
|
|
8741
8763
|
if (code === 0) {
|
|
@@ -10143,8 +10165,14 @@ var Daemon = class _Daemon {
|
|
|
10143
10165
|
const cmd = message.command;
|
|
10144
10166
|
console.log(`[Daemon] EP912: Received agent command for ${projectId}:`, cmd.action);
|
|
10145
10167
|
client.updateActivity();
|
|
10168
|
+
let daemonChunkCount = 0;
|
|
10169
|
+
const daemonStreamStart = Date.now();
|
|
10146
10170
|
const createStreamingCallbacks = (sessionId, commandId) => ({
|
|
10147
10171
|
onChunk: async (chunk) => {
|
|
10172
|
+
daemonChunkCount++;
|
|
10173
|
+
if (daemonChunkCount <= 5) {
|
|
10174
|
+
console.log(`[Daemon] EP1191: Forwarding chunk ${daemonChunkCount} via WebSocket - ${chunk.length} chars`);
|
|
10175
|
+
}
|
|
10148
10176
|
try {
|
|
10149
10177
|
await client.send({
|
|
10150
10178
|
type: "agent_result",
|
|
@@ -10156,6 +10184,8 @@ var Daemon = class _Daemon {
|
|
|
10156
10184
|
}
|
|
10157
10185
|
},
|
|
10158
10186
|
onComplete: async (claudeSessionId) => {
|
|
10187
|
+
const duration = Date.now() - daemonStreamStart;
|
|
10188
|
+
console.log(`[Daemon] EP1191: Stream complete - ${daemonChunkCount} chunks forwarded in ${duration}ms`);
|
|
10159
10189
|
try {
|
|
10160
10190
|
await client.send({
|
|
10161
10191
|
type: "agent_result",
|
|
@@ -10167,6 +10197,8 @@ var Daemon = class _Daemon {
|
|
|
10167
10197
|
}
|
|
10168
10198
|
},
|
|
10169
10199
|
onError: async (error) => {
|
|
10200
|
+
const duration = Date.now() - daemonStreamStart;
|
|
10201
|
+
console.log(`[Daemon] EP1191: Stream error after ${daemonChunkCount} chunks in ${duration}ms - ${error}`);
|
|
10170
10202
|
try {
|
|
10171
10203
|
await client.send({
|
|
10172
10204
|
type: "agent_result",
|
|
@@ -10924,130 +10956,8 @@ var Daemon = class _Daemon {
|
|
|
10924
10956
|
}
|
|
10925
10957
|
}
|
|
10926
10958
|
// EP1025: Removed cleanupModuleWorktree - was dead code (never called).
|
|
10927
|
-
//
|
|
10928
|
-
//
|
|
10929
|
-
/**
|
|
10930
|
-
* EP1047: Process pending cleanup queue entries for this machine
|
|
10931
|
-
*
|
|
10932
|
-
* @deprecated EP1091: No longer called on connect. Server-side cron now processes
|
|
10933
|
-
* the cleanup queue and sends WebSocket commands to connected daemons. This is
|
|
10934
|
-
* more reliable than daemon-side polling since it works even if daemon stays connected.
|
|
10935
|
-
*
|
|
10936
|
-
* Kept for potential manual invocation or debugging purposes.
|
|
10937
|
-
*
|
|
10938
|
-
* Flow:
|
|
10939
|
-
* 1. Query server for pending cleanup tasks for this machine
|
|
10940
|
-
* 2. For each task, attempt worktree removal
|
|
10941
|
-
* 3. Report success/failure back to server
|
|
10942
|
-
*/
|
|
10943
|
-
async reconcilePendingCleanups(projectId, projectPath) {
|
|
10944
|
-
if (!this.machineUuid) {
|
|
10945
|
-
console.log("[Daemon] EP1047: Cannot reconcile cleanups - machineUuid not available yet");
|
|
10946
|
-
return;
|
|
10947
|
-
}
|
|
10948
|
-
console.log(`[Daemon] EP1047: Checking for pending cleanup tasks for machine ${this.machineUuid}`);
|
|
10949
|
-
try {
|
|
10950
|
-
const config = await (0, import_core13.loadConfig)();
|
|
10951
|
-
if (!config) {
|
|
10952
|
-
console.log("[Daemon] EP1047: No config loaded, skipping cleanup reconciliation");
|
|
10953
|
-
return;
|
|
10954
|
-
}
|
|
10955
|
-
const apiUrl = config.api_url || "https://episoda.dev";
|
|
10956
|
-
const controller = new AbortController();
|
|
10957
|
-
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
10958
|
-
let response;
|
|
10959
|
-
try {
|
|
10960
|
-
response = await fetchWithAuth(
|
|
10961
|
-
`${apiUrl}/api/cli/background-ops?machine_id=${this.machineUuid}`,
|
|
10962
|
-
{ signal: controller.signal }
|
|
10963
|
-
);
|
|
10964
|
-
} finally {
|
|
10965
|
-
clearTimeout(timeoutId);
|
|
10966
|
-
}
|
|
10967
|
-
if (!response.ok) {
|
|
10968
|
-
console.warn(`[Daemon] EP1051: Failed to fetch background operations: ${response.status}`);
|
|
10969
|
-
return;
|
|
10970
|
-
}
|
|
10971
|
-
const data = await response.json();
|
|
10972
|
-
const tasks = data.tasks || [];
|
|
10973
|
-
if (tasks.length === 0) {
|
|
10974
|
-
console.log("[Daemon] EP1051: No pending background operations");
|
|
10975
|
-
return;
|
|
10976
|
-
}
|
|
10977
|
-
console.log(`[Daemon] EP1051: Processing ${tasks.length} pending operation(s)`);
|
|
10978
|
-
const projectRoot = await findProjectRoot(projectPath);
|
|
10979
|
-
if (!projectRoot) {
|
|
10980
|
-
console.warn("[Daemon] EP1051: Could not find project root, skipping operation reconciliation");
|
|
10981
|
-
return;
|
|
10982
|
-
}
|
|
10983
|
-
const worktreeManager = new WorktreeManager(projectRoot);
|
|
10984
|
-
if (!await worktreeManager.initialize()) {
|
|
10985
|
-
console.warn("[Daemon] EP1051: Failed to initialize worktree manager");
|
|
10986
|
-
return;
|
|
10987
|
-
}
|
|
10988
|
-
for (const task of tasks) {
|
|
10989
|
-
const moduleUid = task.payload.module_uid || task.target_id;
|
|
10990
|
-
console.log(`[Daemon] EP1051: Processing ${task.operation_type} for ${moduleUid} (task ${task.id})`);
|
|
10991
|
-
try {
|
|
10992
|
-
if (task.operation_type === "worktree_cleanup") {
|
|
10993
|
-
const result = await worktreeManager.removeWorktree(moduleUid, true);
|
|
10994
|
-
if (result.success) {
|
|
10995
|
-
console.log(`[Daemon] EP1051: Successfully cleaned up worktree for ${moduleUid}`);
|
|
10996
|
-
await this.reportOperationResult(apiUrl, task.id, "complete");
|
|
10997
|
-
} else {
|
|
10998
|
-
if (result.error?.includes("not found") || result.error?.includes("No worktree found")) {
|
|
10999
|
-
console.log(`[Daemon] EP1051: Worktree ${moduleUid} already removed, marking complete`);
|
|
11000
|
-
await this.reportOperationResult(apiUrl, task.id, "complete");
|
|
11001
|
-
} else {
|
|
11002
|
-
console.warn(`[Daemon] EP1051: Cleanup failed for ${moduleUid}: ${result.error}`);
|
|
11003
|
-
await this.reportOperationResult(apiUrl, task.id, "retry", {
|
|
11004
|
-
code: "OPERATION_ERROR",
|
|
11005
|
-
message: result.error || "Unknown error"
|
|
11006
|
-
});
|
|
11007
|
-
}
|
|
11008
|
-
}
|
|
11009
|
-
} else {
|
|
11010
|
-
console.warn(`[Daemon] EP1051: Unknown operation type: ${task.operation_type}`);
|
|
11011
|
-
await this.reportOperationResult(apiUrl, task.id, "fail", {
|
|
11012
|
-
code: "UNKNOWN_OPERATION_TYPE",
|
|
11013
|
-
message: `Daemon cannot process operation type: ${task.operation_type}`
|
|
11014
|
-
});
|
|
11015
|
-
}
|
|
11016
|
-
} catch (error) {
|
|
11017
|
-
console.error(`[Daemon] EP1051: Error processing operation for ${moduleUid}:`, error.message);
|
|
11018
|
-
await this.reportOperationResult(apiUrl, task.id, "retry", {
|
|
11019
|
-
code: "OPERATION_EXCEPTION",
|
|
11020
|
-
message: error.message
|
|
11021
|
-
});
|
|
11022
|
-
}
|
|
11023
|
-
}
|
|
11024
|
-
console.log("[Daemon] EP1051: Operation reconciliation complete");
|
|
11025
|
-
} catch (error) {
|
|
11026
|
-
console.error("[Daemon] EP1051: Operation reconciliation error:", error instanceof Error ? error.message : error);
|
|
11027
|
-
throw error;
|
|
11028
|
-
}
|
|
11029
|
-
}
|
|
11030
|
-
/**
|
|
11031
|
-
* EP1051: Report background operation result to server
|
|
11032
|
-
*/
|
|
11033
|
-
async reportOperationResult(apiUrl, taskId, action, error) {
|
|
11034
|
-
try {
|
|
11035
|
-
const response = await fetchWithAuth(`${apiUrl}/api/cli/background-ops`, {
|
|
11036
|
-
method: "POST",
|
|
11037
|
-
headers: { "Content-Type": "application/json" },
|
|
11038
|
-
body: JSON.stringify({
|
|
11039
|
-
task_id: taskId,
|
|
11040
|
-
action,
|
|
11041
|
-
error
|
|
11042
|
-
})
|
|
11043
|
-
});
|
|
11044
|
-
if (!response.ok) {
|
|
11045
|
-
console.warn(`[Daemon] EP1051: Failed to report operation result: ${response.status}`);
|
|
11046
|
-
}
|
|
11047
|
-
} catch (err) {
|
|
11048
|
-
console.warn(`[Daemon] EP1051: Error reporting operation result: ${err.message}`);
|
|
11049
|
-
}
|
|
11050
|
-
}
|
|
10959
|
+
// EP1188: Background ops queue removed - worktree cleanup now handled by Inngest events
|
|
10960
|
+
// (worktree/cleanup) which send WebSocket commands to connected daemons via ws-proxy.
|
|
11051
10961
|
/**
|
|
11052
10962
|
* EP1002: Handle worktree_setup command from server
|
|
11053
10963
|
* This provides a unified setup flow for both local and cloud environments.
|