@junctionpanel/server 0.1.33 → 0.1.35
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/dist/server/client/daemon-client.d.ts +2 -0
- package/dist/server/client/daemon-client.d.ts.map +1 -1
- package/dist/server/client/daemon-client.js +2 -0
- package/dist/server/client/daemon-client.js.map +1 -1
- package/dist/server/server/agent/agent-management-mcp.d.ts.map +1 -1
- package/dist/server/server/agent/agent-management-mcp.js +44 -7
- package/dist/server/server/agent/agent-management-mcp.js.map +1 -1
- package/dist/server/server/agent/agent-manager.d.ts +5 -0
- package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
- package/dist/server/server/agent/agent-manager.js +66 -12
- package/dist/server/server/agent/agent-manager.js.map +1 -1
- package/dist/server/server/agent/agent-permission-fingerprint.d.ts +6 -0
- package/dist/server/server/agent/agent-permission-fingerprint.d.ts.map +1 -0
- package/dist/server/server/agent/agent-permission-fingerprint.js +28 -0
- package/dist/server/server/agent/agent-permission-fingerprint.js.map +1 -0
- package/dist/server/server/agent/agent-projections.d.ts +1 -0
- package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
- package/dist/server/server/agent/agent-projections.js +61 -3
- package/dist/server/server/agent/agent-projections.js.map +1 -1
- package/dist/server/server/agent/agent-sdk-types.d.ts +5 -0
- package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.d.ts +13 -0
- package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
- package/dist/server/server/agent/agent-storage.js +4 -2
- package/dist/server/server/agent/agent-storage.js.map +1 -1
- package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
- package/dist/server/server/agent/mcp-server.js +44 -7
- package/dist/server/server/agent/mcp-server.js.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/claude-agent.js +38 -90
- package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
- package/dist/server/server/agent/providers/claude-cli-capabilities.d.ts +50 -0
- package/dist/server/server/agent/providers/claude-cli-capabilities.d.ts.map +1 -0
- package/dist/server/server/agent/providers/claude-cli-capabilities.js +247 -0
- package/dist/server/server/agent/providers/claude-cli-capabilities.js.map +1 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/codex-app-server-agent.js +4 -0
- package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
- package/dist/server/server/agent/providers/gemini-agent.d.ts +287 -1
- package/dist/server/server/agent/providers/gemini-agent.d.ts.map +1 -1
- package/dist/server/server/agent/providers/gemini-agent.js +255 -16
- package/dist/server/server/agent/providers/gemini-agent.js.map +1 -1
- package/dist/server/server/daemon-doctor.d.ts +5 -0
- package/dist/server/server/daemon-doctor.d.ts.map +1 -1
- package/dist/server/server/daemon-doctor.js +26 -0
- package/dist/server/server/daemon-doctor.js.map +1 -1
- package/dist/server/server/file-explorer/service.d.ts +1 -0
- package/dist/server/server/file-explorer/service.d.ts.map +1 -1
- package/dist/server/server/file-explorer/service.js +36 -0
- package/dist/server/server/file-explorer/service.js.map +1 -1
- package/dist/server/server/session.d.ts +1 -0
- package/dist/server/server/session.d.ts.map +1 -1
- package/dist/server/server/session.js +82 -71
- package/dist/server/server/session.js.map +1 -1
- package/dist/server/server/worktree-bootstrap.d.ts +2 -1
- package/dist/server/server/worktree-bootstrap.d.ts.map +1 -1
- package/dist/server/server/worktree-bootstrap.js +1 -0
- package/dist/server/server/worktree-bootstrap.js.map +1 -1
- package/dist/server/shared/messages.d.ts +424 -218
- package/dist/server/shared/messages.d.ts.map +1 -1
- package/dist/server/shared/messages.js +9 -0
- package/dist/server/shared/messages.js.map +1 -1
- package/dist/server/utils/checkout-git.d.ts +14 -0
- package/dist/server/utils/checkout-git.d.ts.map +1 -1
- package/dist/server/utils/checkout-git.js +73 -44
- package/dist/server/utils/checkout-git.js.map +1 -1
- package/dist/server/utils/worktree-metadata.d.ts +30 -0
- package/dist/server/utils/worktree-metadata.d.ts.map +1 -1
- package/dist/server/utils/worktree-metadata.js +38 -9
- package/dist/server/utils/worktree-metadata.js.map +1 -1
- package/dist/server/utils/worktree.d.ts +7 -3
- package/dist/server/utils/worktree.d.ts.map +1 -1
- package/dist/server/utils/worktree.js +91 -47
- package/dist/server/utils/worktree.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as acp from "@agentclientprotocol/sdk";
|
|
2
2
|
import { execSync, spawn, spawnSync } from "node:child_process";
|
|
3
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { access, readdir, readFile } from "node:fs/promises";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { Readable as NodeReadable, Writable as NodeWritable } from "node:stream";
|
|
7
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
7
8
|
import { z } from "zod";
|
|
8
9
|
import { applyProviderEnv, isProviderCommandAvailable, resolveProviderCommandPrefix, } from "../provider-launch-config.js";
|
|
9
10
|
import { ToolEditInputSchema, ToolEditOutputSchema, ToolReadInputSchema, ToolReadOutputSchema, ToolSearchInputSchema, ToolShellInputSchema, ToolShellOutputSchema, ToolWriteInputSchema, ToolWriteOutputSchema, toEditToolDetail, toReadToolDetail, toSearchToolDetail, toShellToolDetail, toWriteToolDetail, } from "./tool-call-detail-primitives.js";
|
|
@@ -13,6 +14,8 @@ const GEMINI_MODES = ["default", "auto_edit", "yolo", "plan"];
|
|
|
13
14
|
const GEMINI_GLOBAL_DIR = path.join(homedir(), ".gemini");
|
|
14
15
|
const GEMINI_TMP_DIR = path.join(GEMINI_GLOBAL_DIR, "tmp");
|
|
15
16
|
const GEMINI_HISTORY_FALLBACK_IDLE_MS = 1000;
|
|
17
|
+
const GEMINI_RECORDED_SESSION_POLL_INTERVAL_MS = 100;
|
|
18
|
+
const GEMINI_RECORDED_SESSION_DISCOVERY_TIMEOUT_MS = 200;
|
|
16
19
|
const GEMINI_CAPABILITIES = {
|
|
17
20
|
supportsStreaming: true,
|
|
18
21
|
supportsSessionPersistence: true,
|
|
@@ -98,6 +101,22 @@ const GeminiRecordedThoughtSchema = z
|
|
|
98
101
|
text: z.string().optional(),
|
|
99
102
|
})
|
|
100
103
|
.passthrough();
|
|
104
|
+
const GeminiRecordedTokensSchema = z.preprocess((value) => {
|
|
105
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
return value;
|
|
109
|
+
}, z
|
|
110
|
+
.object({
|
|
111
|
+
input: z.number().nonnegative().optional(),
|
|
112
|
+
output: z.number().nonnegative().optional(),
|
|
113
|
+
cached: z.number().nonnegative().optional(),
|
|
114
|
+
thoughts: z.number().nonnegative().optional(),
|
|
115
|
+
tool: z.number().nonnegative().optional(),
|
|
116
|
+
total: z.number().nonnegative().optional(),
|
|
117
|
+
})
|
|
118
|
+
.passthrough()
|
|
119
|
+
.optional());
|
|
101
120
|
const GeminiRecordedToolCallSchema = z
|
|
102
121
|
.object({
|
|
103
122
|
id: z.string().optional(),
|
|
@@ -112,10 +131,13 @@ const GeminiRecordedMessageSchema = z
|
|
|
112
131
|
.object({
|
|
113
132
|
id: z.string().optional(),
|
|
114
133
|
type: z.string(),
|
|
134
|
+
timestamp: z.string().optional(),
|
|
115
135
|
content: z.unknown().optional(),
|
|
116
136
|
displayContent: z.unknown().optional(),
|
|
117
137
|
thoughts: z.array(GeminiRecordedThoughtSchema).optional(),
|
|
118
138
|
toolCalls: z.array(GeminiRecordedToolCallSchema).optional(),
|
|
139
|
+
tokens: GeminiRecordedTokensSchema.optional(),
|
|
140
|
+
model: z.string().optional(),
|
|
119
141
|
})
|
|
120
142
|
.passthrough();
|
|
121
143
|
const GeminiRecordedSessionSchema = z
|
|
@@ -463,20 +485,22 @@ function toGeminiModelDefinitions() {
|
|
|
463
485
|
async function readGeminiSessionFile(filePath) {
|
|
464
486
|
try {
|
|
465
487
|
const raw = await readFile(filePath, "utf8");
|
|
466
|
-
|
|
467
|
-
const session = GeminiRecordedSessionSchema.safeParse(parsed);
|
|
468
|
-
if (!session.success) {
|
|
469
|
-
return null;
|
|
470
|
-
}
|
|
471
|
-
if (session.data.kind === "subagent") {
|
|
472
|
-
return null;
|
|
473
|
-
}
|
|
474
|
-
return session.data;
|
|
488
|
+
return parseGeminiRecordedSession(JSON.parse(raw));
|
|
475
489
|
}
|
|
476
490
|
catch {
|
|
477
491
|
return null;
|
|
478
492
|
}
|
|
479
493
|
}
|
|
494
|
+
export function parseGeminiRecordedSession(raw) {
|
|
495
|
+
const session = GeminiRecordedSessionSchema.safeParse(raw);
|
|
496
|
+
if (!session.success) {
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
if (session.data.kind === "subagent") {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
return session.data;
|
|
503
|
+
}
|
|
480
504
|
function extractRecordedContentText(content) {
|
|
481
505
|
if (typeof content === "string") {
|
|
482
506
|
return content;
|
|
@@ -529,8 +553,22 @@ function extractRecordedThoughtText(thought) {
|
|
|
529
553
|
}
|
|
530
554
|
export function buildGeminiHistoryTimeline(session) {
|
|
531
555
|
const items = [];
|
|
556
|
+
let pendingTurnMessages = [];
|
|
557
|
+
const flushPendingTurnSummary = () => {
|
|
558
|
+
const summary = summarizeGeminiRecordedMessages(pendingTurnMessages);
|
|
559
|
+
pendingTurnMessages = [];
|
|
560
|
+
if (!summary) {
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
items.push({
|
|
564
|
+
type: "turn_summary",
|
|
565
|
+
usage: summary.usage,
|
|
566
|
+
modelId: summary.modelId,
|
|
567
|
+
});
|
|
568
|
+
};
|
|
532
569
|
for (const message of session.messages) {
|
|
533
570
|
if (message.type === "user") {
|
|
571
|
+
flushPendingTurnSummary();
|
|
534
572
|
const text = extractRecordedContentText(message.content ?? message.displayContent).trim();
|
|
535
573
|
if (text.length > 0) {
|
|
536
574
|
items.push({
|
|
@@ -542,8 +580,10 @@ export function buildGeminiHistoryTimeline(session) {
|
|
|
542
580
|
continue;
|
|
543
581
|
}
|
|
544
582
|
if (message.type !== "gemini") {
|
|
583
|
+
flushPendingTurnSummary();
|
|
545
584
|
continue;
|
|
546
585
|
}
|
|
586
|
+
pendingTurnMessages.push(message);
|
|
547
587
|
const assistantText = extractRecordedContentText(message.content ?? message.displayContent).trim();
|
|
548
588
|
if (assistantText.length > 0) {
|
|
549
589
|
items.push({ type: "assistant_message", text: assistantText });
|
|
@@ -568,8 +608,107 @@ export function buildGeminiHistoryTimeline(session) {
|
|
|
568
608
|
}));
|
|
569
609
|
}
|
|
570
610
|
}
|
|
611
|
+
flushPendingTurnSummary();
|
|
571
612
|
return items;
|
|
572
613
|
}
|
|
614
|
+
export function summarizeGeminiRecordedMessages(messages) {
|
|
615
|
+
let inputTokens = 0;
|
|
616
|
+
let cachedInputTokens = 0;
|
|
617
|
+
let outputTokens = 0;
|
|
618
|
+
let hasUsage = false;
|
|
619
|
+
let modelId = null;
|
|
620
|
+
for (const message of messages) {
|
|
621
|
+
if (message.type !== "gemini") {
|
|
622
|
+
continue;
|
|
623
|
+
}
|
|
624
|
+
const usage = toGeminiRecordedUsage(message);
|
|
625
|
+
if (usage) {
|
|
626
|
+
inputTokens += usage.inputTokens ?? 0;
|
|
627
|
+
cachedInputTokens += usage.cachedInputTokens ?? 0;
|
|
628
|
+
outputTokens += usage.outputTokens ?? 0;
|
|
629
|
+
hasUsage = true;
|
|
630
|
+
}
|
|
631
|
+
const recordedModelId = nonEmptyString(message.model);
|
|
632
|
+
if (recordedModelId) {
|
|
633
|
+
modelId = recordedModelId;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
if (!hasUsage) {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
usage: {
|
|
641
|
+
...(inputTokens > 0 ? { inputTokens } : {}),
|
|
642
|
+
...(cachedInputTokens > 0 ? { cachedInputTokens } : {}),
|
|
643
|
+
...(outputTokens > 0 ? { outputTokens } : {}),
|
|
644
|
+
},
|
|
645
|
+
modelId,
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
export function summarizeGeminiRecordedTurn(session, turnStartedAt) {
|
|
649
|
+
return summarizeGeminiRecordedMessages(getGeminiRecordedTurnMessages(session, turnStartedAt));
|
|
650
|
+
}
|
|
651
|
+
function getGeminiRecordedTurnMessages(session, turnStartedAt) {
|
|
652
|
+
const turnStartMs = turnStartedAt.getTime();
|
|
653
|
+
const turnMessages = [];
|
|
654
|
+
let collecting = false;
|
|
655
|
+
for (const message of session.messages) {
|
|
656
|
+
const timestampMs = typeof message.timestamp === "string" ? Date.parse(message.timestamp) : Number.NaN;
|
|
657
|
+
const isInTurn = Number.isFinite(timestampMs) && timestampMs >= turnStartMs;
|
|
658
|
+
if (isInTurn) {
|
|
659
|
+
collecting = true;
|
|
660
|
+
turnMessages.push(message);
|
|
661
|
+
continue;
|
|
662
|
+
}
|
|
663
|
+
if (collecting && message.type === "gemini") {
|
|
664
|
+
turnMessages.push(message);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return turnMessages;
|
|
668
|
+
}
|
|
669
|
+
function hasGeminiRecordedAssistantMessage(messages) {
|
|
670
|
+
return messages.some((message) => message.type === "gemini");
|
|
671
|
+
}
|
|
672
|
+
function countGeminiRecordedHistoryEvents(session) {
|
|
673
|
+
let count = 0;
|
|
674
|
+
for (const message of session.messages) {
|
|
675
|
+
if (message.type === "user") {
|
|
676
|
+
const text = extractRecordedContentText(message.content ?? message.displayContent).trim();
|
|
677
|
+
if (text.length > 0) {
|
|
678
|
+
count += 1;
|
|
679
|
+
}
|
|
680
|
+
continue;
|
|
681
|
+
}
|
|
682
|
+
if (message.type !== "gemini") {
|
|
683
|
+
continue;
|
|
684
|
+
}
|
|
685
|
+
const assistantText = extractRecordedContentText(message.content ?? message.displayContent).trim();
|
|
686
|
+
if (assistantText.length > 0) {
|
|
687
|
+
count += 1;
|
|
688
|
+
}
|
|
689
|
+
for (const thought of message.thoughts ?? []) {
|
|
690
|
+
const text = extractRecordedThoughtText(thought);
|
|
691
|
+
if (text) {
|
|
692
|
+
count += 1;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
count += (message.toolCalls ?? []).length;
|
|
696
|
+
}
|
|
697
|
+
return count;
|
|
698
|
+
}
|
|
699
|
+
function toGeminiRecordedUsage(message) {
|
|
700
|
+
const inputTokens = message.tokens?.input ?? 0;
|
|
701
|
+
const cachedInputTokens = message.tokens?.cached ?? 0;
|
|
702
|
+
const outputTokens = message.tokens?.output ?? 0;
|
|
703
|
+
if (inputTokens === 0 && cachedInputTokens === 0 && outputTokens === 0) {
|
|
704
|
+
return undefined;
|
|
705
|
+
}
|
|
706
|
+
return {
|
|
707
|
+
...(inputTokens > 0 ? { inputTokens } : {}),
|
|
708
|
+
...(cachedInputTokens > 0 ? { cachedInputTokens } : {}),
|
|
709
|
+
...(outputTokens > 0 ? { outputTokens } : {}),
|
|
710
|
+
};
|
|
711
|
+
}
|
|
573
712
|
async function listKnownGeminiProjects() {
|
|
574
713
|
let entries;
|
|
575
714
|
try {
|
|
@@ -598,11 +737,14 @@ async function resolveGeminiProjectTempDirForCwd(cwd) {
|
|
|
598
737
|
const projects = await listKnownGeminiProjects();
|
|
599
738
|
return projects.find((project) => project.cwd === cwd)?.tempDir ?? null;
|
|
600
739
|
}
|
|
601
|
-
async function
|
|
740
|
+
async function findGeminiRecordedSessionForCwd(cwd, sessionId) {
|
|
602
741
|
const tempDir = await resolveGeminiProjectTempDirForCwd(cwd);
|
|
603
742
|
if (!tempDir) {
|
|
604
743
|
return null;
|
|
605
744
|
}
|
|
745
|
+
return findGeminiRecordedSessionForTempDir(tempDir, sessionId);
|
|
746
|
+
}
|
|
747
|
+
async function findGeminiRecordedSessionForTempDir(tempDir, sessionId) {
|
|
606
748
|
const chatsDir = path.join(tempDir, "chats");
|
|
607
749
|
let chatFiles;
|
|
608
750
|
try {
|
|
@@ -615,13 +757,26 @@ async function loadGeminiRecordedSessionForCwd(cwd, sessionId) {
|
|
|
615
757
|
if (!fileName.startsWith("session-") || !fileName.endsWith(".json")) {
|
|
616
758
|
continue;
|
|
617
759
|
}
|
|
618
|
-
const
|
|
760
|
+
const filePath = path.join(chatsDir, fileName);
|
|
761
|
+
const session = await readGeminiSessionFile(filePath);
|
|
619
762
|
if (session?.sessionId === sessionId) {
|
|
620
|
-
return
|
|
763
|
+
return {
|
|
764
|
+
filePath,
|
|
765
|
+
session,
|
|
766
|
+
};
|
|
621
767
|
}
|
|
622
768
|
}
|
|
623
769
|
return null;
|
|
624
770
|
}
|
|
771
|
+
async function geminiSessionFileExists(filePath) {
|
|
772
|
+
try {
|
|
773
|
+
await access(filePath);
|
|
774
|
+
return true;
|
|
775
|
+
}
|
|
776
|
+
catch {
|
|
777
|
+
return false;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
625
780
|
async function listGeminiSessionsForProject(params) {
|
|
626
781
|
const chatsDir = path.join(params.tempDir, "chats");
|
|
627
782
|
let chatFiles;
|
|
@@ -1023,6 +1178,8 @@ class GeminiAgentSession {
|
|
|
1023
1178
|
this.expectedHistoryEventCount = null;
|
|
1024
1179
|
this.capturedHistoryEventCount = 0;
|
|
1025
1180
|
this.currentRunInterrupted = false;
|
|
1181
|
+
this.hasObservedRecordedSession = false;
|
|
1182
|
+
this.recordedSessionFilePath = null;
|
|
1026
1183
|
this.config = config;
|
|
1027
1184
|
this.logger = logger.child({ sessionProvider: GEMINI_PROVIDER });
|
|
1028
1185
|
this.runtimeSettings = runtimeSettings;
|
|
@@ -1049,6 +1206,7 @@ class GeminiAgentSession {
|
|
|
1049
1206
|
const timeline = [];
|
|
1050
1207
|
let finalText = "";
|
|
1051
1208
|
let canceled = false;
|
|
1209
|
+
let usage;
|
|
1052
1210
|
for await (const event of this.stream(prompt, options)) {
|
|
1053
1211
|
if (event.type === "timeline") {
|
|
1054
1212
|
timeline.push(event.item);
|
|
@@ -1056,6 +1214,9 @@ class GeminiAgentSession {
|
|
|
1056
1214
|
finalText += event.item.text;
|
|
1057
1215
|
}
|
|
1058
1216
|
}
|
|
1217
|
+
else if (event.type === "turn_completed") {
|
|
1218
|
+
usage = event.usage;
|
|
1219
|
+
}
|
|
1059
1220
|
else if (event.type === "turn_failed") {
|
|
1060
1221
|
throw new Error(event.error);
|
|
1061
1222
|
}
|
|
@@ -1066,6 +1227,7 @@ class GeminiAgentSession {
|
|
|
1066
1227
|
return {
|
|
1067
1228
|
sessionId: this.sessionId ?? "",
|
|
1068
1229
|
finalText,
|
|
1230
|
+
...(usage ? { usage } : {}),
|
|
1069
1231
|
timeline,
|
|
1070
1232
|
...(canceled ? { canceled } : {}),
|
|
1071
1233
|
};
|
|
@@ -1081,6 +1243,7 @@ class GeminiAgentSession {
|
|
|
1081
1243
|
const queue = new AsyncEventQueue();
|
|
1082
1244
|
this.activeTurnQueue = queue;
|
|
1083
1245
|
this.currentRunInterrupted = false;
|
|
1246
|
+
const turnStartedAt = new Date();
|
|
1084
1247
|
queue.push({
|
|
1085
1248
|
type: "turn_started",
|
|
1086
1249
|
provider: GEMINI_PROVIDER,
|
|
@@ -1090,7 +1253,7 @@ class GeminiAgentSession {
|
|
|
1090
1253
|
sessionId: this.sessionId,
|
|
1091
1254
|
prompt: toGeminiPromptBlocks(prompt),
|
|
1092
1255
|
})
|
|
1093
|
-
.then((response) => {
|
|
1256
|
+
.then(async (response) => {
|
|
1094
1257
|
if (response.stopReason === "cancelled") {
|
|
1095
1258
|
queue.push({
|
|
1096
1259
|
type: "turn_canceled",
|
|
@@ -1099,9 +1262,17 @@ class GeminiAgentSession {
|
|
|
1099
1262
|
});
|
|
1100
1263
|
}
|
|
1101
1264
|
else {
|
|
1265
|
+
// ACP does not report usage, so we synthesize it from Gemini's recorded session file.
|
|
1266
|
+
const recordedTurnSummary = await this.readRecordedTurnSummary(turnStartedAt);
|
|
1102
1267
|
queue.push({
|
|
1103
1268
|
type: "turn_completed",
|
|
1104
1269
|
provider: GEMINI_PROVIDER,
|
|
1270
|
+
...(recordedTurnSummary
|
|
1271
|
+
? {
|
|
1272
|
+
usage: recordedTurnSummary.usage,
|
|
1273
|
+
modelId: recordedTurnSummary.modelId,
|
|
1274
|
+
}
|
|
1275
|
+
: {}),
|
|
1105
1276
|
});
|
|
1106
1277
|
}
|
|
1107
1278
|
queue.close();
|
|
@@ -1379,8 +1550,11 @@ class GeminiAgentSession {
|
|
|
1379
1550
|
},
|
|
1380
1551
|
});
|
|
1381
1552
|
if (this.sessionId) {
|
|
1382
|
-
const
|
|
1383
|
-
|
|
1553
|
+
const recordedSessionMatch = await findGeminiRecordedSessionForCwd(this.config.cwd, this.sessionId);
|
|
1554
|
+
const recordedSession = recordedSessionMatch?.session ?? null;
|
|
1555
|
+
this.hasObservedRecordedSession = recordedSession !== null;
|
|
1556
|
+
this.recordedSessionFilePath = recordedSessionMatch?.filePath ?? null;
|
|
1557
|
+
this.beginHistoryCapture(recordedSession ? countGeminiRecordedHistoryEvents(recordedSession) : null);
|
|
1384
1558
|
const response = await this.connection.loadSession({
|
|
1385
1559
|
sessionId: this.sessionId,
|
|
1386
1560
|
cwd: this.config.cwd,
|
|
@@ -1527,6 +1701,71 @@ class GeminiAgentSession {
|
|
|
1527
1701
|
emitLiveEvent(event) {
|
|
1528
1702
|
this.activeTurnQueue?.push(event);
|
|
1529
1703
|
}
|
|
1704
|
+
async readRecordedTurnSummary(turnStartedAt) {
|
|
1705
|
+
if (!this.sessionId) {
|
|
1706
|
+
return null;
|
|
1707
|
+
}
|
|
1708
|
+
const tempDir = await resolveGeminiProjectTempDirForCwd(this.config.cwd);
|
|
1709
|
+
if (!tempDir) {
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
const deadline = Date.now() + GEMINI_HISTORY_FALLBACK_IDLE_MS;
|
|
1713
|
+
const discoveryDeadline = Date.now() +
|
|
1714
|
+
(this.hasObservedRecordedSession && !this.recordedSessionFilePath
|
|
1715
|
+
? GEMINI_RECORDED_SESSION_DISCOVERY_TIMEOUT_MS
|
|
1716
|
+
: GEMINI_HISTORY_FALLBACK_IDLE_MS);
|
|
1717
|
+
let latestFingerprint = null;
|
|
1718
|
+
let observedSummary = false;
|
|
1719
|
+
let latestEmptyTurnFingerprint = null;
|
|
1720
|
+
let observedEmptyTurn = false;
|
|
1721
|
+
let sawRecordedSession = false;
|
|
1722
|
+
while (Date.now() <= deadline) {
|
|
1723
|
+
let recordedSession = null;
|
|
1724
|
+
if (this.recordedSessionFilePath) {
|
|
1725
|
+
recordedSession = await readGeminiSessionFile(this.recordedSessionFilePath);
|
|
1726
|
+
if (!recordedSession && !(await geminiSessionFileExists(this.recordedSessionFilePath))) {
|
|
1727
|
+
this.recordedSessionFilePath = null;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
if (!recordedSession) {
|
|
1731
|
+
const recordedSessionMatch = await findGeminiRecordedSessionForTempDir(tempDir, this.sessionId);
|
|
1732
|
+
recordedSession = recordedSessionMatch?.session ?? null;
|
|
1733
|
+
if (recordedSessionMatch) {
|
|
1734
|
+
this.recordedSessionFilePath = recordedSessionMatch.filePath;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
if (recordedSession) {
|
|
1738
|
+
sawRecordedSession = true;
|
|
1739
|
+
this.hasObservedRecordedSession = true;
|
|
1740
|
+
const turnMessages = getGeminiRecordedTurnMessages(recordedSession, turnStartedAt);
|
|
1741
|
+
const nextSummary = summarizeGeminiRecordedMessages(turnMessages);
|
|
1742
|
+
if (nextSummary) {
|
|
1743
|
+
const nextFingerprint = JSON.stringify(nextSummary);
|
|
1744
|
+
if (observedSummary && nextFingerprint === latestFingerprint) {
|
|
1745
|
+
return nextSummary;
|
|
1746
|
+
}
|
|
1747
|
+
latestFingerprint = nextFingerprint;
|
|
1748
|
+
observedSummary = true;
|
|
1749
|
+
observedEmptyTurn = false;
|
|
1750
|
+
latestEmptyTurnFingerprint = null;
|
|
1751
|
+
}
|
|
1752
|
+
else if (hasGeminiRecordedAssistantMessage(turnMessages)) {
|
|
1753
|
+
const nextEmptyTurnFingerprint = JSON.stringify(turnMessages);
|
|
1754
|
+
if (observedEmptyTurn &&
|
|
1755
|
+
nextEmptyTurnFingerprint === latestEmptyTurnFingerprint) {
|
|
1756
|
+
return null;
|
|
1757
|
+
}
|
|
1758
|
+
observedEmptyTurn = true;
|
|
1759
|
+
latestEmptyTurnFingerprint = nextEmptyTurnFingerprint;
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
else if (!sawRecordedSession && Date.now() >= discoveryDeadline) {
|
|
1763
|
+
return null;
|
|
1764
|
+
}
|
|
1765
|
+
await delay(GEMINI_RECORDED_SESSION_POLL_INTERVAL_MS);
|
|
1766
|
+
}
|
|
1767
|
+
return null;
|
|
1768
|
+
}
|
|
1530
1769
|
async handlePermissionRequest(params) {
|
|
1531
1770
|
if (this.currentRunInterrupted) {
|
|
1532
1771
|
return {
|