u-foo 3.0.24 → 3.0.26
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/tui/darwin-arm64/ufoo-tui +0 -0
- package/dist/tui/darwin-x64/ufoo-tui +0 -0
- package/dist/tui/linux-arm64/ufoo-tui +0 -0
- package/dist/tui/linux-x64/ufoo-tui +0 -0
- package/package.json +1 -1
- package/src/code/UcodeController.js +2 -10
- package/src/code/agent.js +105 -26
- package/src/code/context/assembler.js +0 -102
- package/src/code/context/transcript.js +5 -0
- package/src/code/conversation/sessionJournal.js +351 -0
- package/src/code/nativeRunner.js +26 -0
- package/src/code/sessionStore.js +9 -21
- package/src/code/taskDecomposer.js +7 -3
- package/src/code/ucodeSlashDispatch.js +1 -1
- package/src/runtime/contracts/uiProtocol.js +2 -0
- package/src/ui/format/index.js +105 -36
- package/src/ui/format/markdownRenderer.js +2 -2
- package/src/ui/rustChatHost.js +2 -0
- package/src/ui/rustUcodeHost.js +106 -30
- package/src/ui/toolMergeBridge.js +5 -7
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -106,27 +106,20 @@ function createUcodeController({
|
|
|
106
106
|
*/
|
|
107
107
|
function createThinkingStatusPublisher(publish, options = {}) {
|
|
108
108
|
const intervalMs = Number(options.intervalMs) > 0 ? Number(options.intervalMs) : 120;
|
|
109
|
-
let tail = "";
|
|
110
109
|
let timer = null;
|
|
111
110
|
let lastFlush = 0;
|
|
112
111
|
|
|
113
|
-
function collapse(text) {
|
|
114
|
-
const raw = String(text || "").replace(/\s+/g, " ").trim();
|
|
115
|
-
if (!raw) return "Thinking…";
|
|
116
|
-
return raw.length > 72 ? `${raw.slice(-72)}` : raw;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
112
|
function flush() {
|
|
120
113
|
timer = null;
|
|
121
114
|
lastFlush = Date.now();
|
|
122
115
|
publish("status.set", {
|
|
123
|
-
text:
|
|
116
|
+
text: "Thinking…",
|
|
124
117
|
busy: true,
|
|
125
118
|
});
|
|
126
119
|
}
|
|
127
120
|
|
|
128
121
|
function onThinkingDelta(chunk) {
|
|
129
|
-
|
|
122
|
+
if (!String(chunk || "")) return;
|
|
130
123
|
const elapsed = Date.now() - lastFlush;
|
|
131
124
|
if (elapsed >= intervalMs) {
|
|
132
125
|
flush();
|
|
@@ -143,7 +136,6 @@ function createThinkingStatusPublisher(publish, options = {}) {
|
|
|
143
136
|
clearTimeout(timer);
|
|
144
137
|
timer = null;
|
|
145
138
|
}
|
|
146
|
-
tail = "";
|
|
147
139
|
lastFlush = 0;
|
|
148
140
|
}
|
|
149
141
|
|
package/src/code/agent.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const fs = require("fs");
|
|
2
2
|
const path = require("path");
|
|
3
|
+
const { randomUUID } = require("crypto");
|
|
3
4
|
const { runToolCall } = require("./dispatch");
|
|
4
5
|
const { runNativeAgentTask } = require("./nativeRunner");
|
|
5
6
|
const { runDecomposedTask } = require("./taskDecomposer");
|
|
@@ -13,13 +14,13 @@ const {
|
|
|
13
14
|
const { buildSkillInjections } = require("./skills");
|
|
14
15
|
const {
|
|
15
16
|
assembleModelContext,
|
|
16
|
-
syncMessagesToTranscript,
|
|
17
17
|
applyContextSideEffects,
|
|
18
18
|
ensureProjectSnapshot,
|
|
19
19
|
recordToolCallInSession,
|
|
20
20
|
commitAfterSegmentEnd,
|
|
21
|
-
sanitizeModelMessages,
|
|
22
21
|
} = require("./context/assembler");
|
|
22
|
+
const { appendTurnMessages } = require("./conversation/sessionJournal");
|
|
23
|
+
const { loadTranscript, transcriptEventsToMessages } = require("./context/transcript");
|
|
23
24
|
const { buildLayeredSystemPrompt } = require("./context/promptLayers");
|
|
24
25
|
const {
|
|
25
26
|
createProjectPreflightContextV2,
|
|
@@ -539,6 +540,7 @@ async function runNaturalLanguageTask(task = "", state = {}, options = {}) {
|
|
|
539
540
|
const analysisTask = isProjectAnalysisTask(taskText);
|
|
540
541
|
const workspaceRoot = String(state.workspaceRoot || process.cwd());
|
|
541
542
|
ensureContextSessionState(state);
|
|
543
|
+
state.sessionId = resolveSessionId(state.sessionId);
|
|
542
544
|
|
|
543
545
|
let projectSnapshot = state.projectSnapshot || null;
|
|
544
546
|
if (analysisTask) {
|
|
@@ -591,6 +593,26 @@ async function runNaturalLanguageTask(task = "", state = {}, options = {}) {
|
|
|
591
593
|
});
|
|
592
594
|
const systemContext = assembled.systemPrompt;
|
|
593
595
|
state.summary = assembled.summary || state.summary;
|
|
596
|
+
const turnId = `turn_${Date.now().toString(36)}_${randomUUID().slice(0, 8)}`;
|
|
597
|
+
const inputAppend = appendTurnMessages(
|
|
598
|
+
workspaceRoot,
|
|
599
|
+
String(state.sessionId || ""),
|
|
600
|
+
turnId,
|
|
601
|
+
[{ role: "user", content: taskText }],
|
|
602
|
+
{ scope: "input" },
|
|
603
|
+
);
|
|
604
|
+
if (!inputAppend.ok) {
|
|
605
|
+
return {
|
|
606
|
+
ok: false,
|
|
607
|
+
summary: "",
|
|
608
|
+
artifacts: [],
|
|
609
|
+
logs: logs.slice(),
|
|
610
|
+
error: `failed to persist conversation input: ${inputAppend.error}`,
|
|
611
|
+
metrics: {},
|
|
612
|
+
streamed: false,
|
|
613
|
+
streamLastChar: "",
|
|
614
|
+
};
|
|
615
|
+
}
|
|
594
616
|
|
|
595
617
|
const onStream = onDelta
|
|
596
618
|
? (delta) => {
|
|
@@ -625,13 +647,9 @@ async function runNaturalLanguageTask(task = "", state = {}, options = {}) {
|
|
|
625
647
|
}
|
|
626
648
|
return state.contextMeter;
|
|
627
649
|
};
|
|
628
|
-
let lastTranscriptBaseline = 0;
|
|
629
650
|
const invokeNative = (sessionIdValue = "", timeoutOverrideMs = timeoutMs) => {
|
|
630
651
|
toolEventsThisAttempt = 0;
|
|
631
652
|
const historyMessages = assembled.messages;
|
|
632
|
-
// Sanitized length matches what nativeRunner clones before appending this
|
|
633
|
-
// turn's user/tool/assistant messages — used as the transcript sync baseline.
|
|
634
|
-
lastTranscriptBaseline = sanitizeModelMessages(historyMessages).length;
|
|
635
653
|
return runNativeAgentImpl({
|
|
636
654
|
workspaceRoot,
|
|
637
655
|
provider,
|
|
@@ -706,12 +724,13 @@ async function runNaturalLanguageTask(task = "", state = {}, options = {}) {
|
|
|
706
724
|
ok: true,
|
|
707
725
|
output: decomposedResult.summary,
|
|
708
726
|
sessionId: state.sessionId,
|
|
709
|
-
|
|
727
|
+
turnItems: decomposedResult.turnItems,
|
|
710
728
|
};
|
|
711
729
|
} else {
|
|
712
730
|
cliRes = {
|
|
713
731
|
ok: false,
|
|
714
732
|
error: decomposedResult.error,
|
|
733
|
+
turnItems: decomposedResult.turnItems,
|
|
715
734
|
};
|
|
716
735
|
}
|
|
717
736
|
} else {
|
|
@@ -729,6 +748,34 @@ async function runNaturalLanguageTask(task = "", state = {}, options = {}) {
|
|
|
729
748
|
}
|
|
730
749
|
}
|
|
731
750
|
|
|
751
|
+
const outputItems = cliRes && Array.isArray(cliRes.turnItems) ? cliRes.turnItems : [];
|
|
752
|
+
if (outputItems.length > 0) {
|
|
753
|
+
const outputAppend = appendTurnMessages(
|
|
754
|
+
workspaceRoot,
|
|
755
|
+
String(state.sessionId || ""),
|
|
756
|
+
turnId,
|
|
757
|
+
outputItems,
|
|
758
|
+
{ scope: "output" },
|
|
759
|
+
);
|
|
760
|
+
if (!outputAppend.ok) {
|
|
761
|
+
return {
|
|
762
|
+
ok: false,
|
|
763
|
+
summary: "",
|
|
764
|
+
artifacts: [],
|
|
765
|
+
logs: logs.slice(),
|
|
766
|
+
error: `failed to persist conversation output: ${outputAppend.error}`,
|
|
767
|
+
metrics: {},
|
|
768
|
+
streamed: Boolean(streamed || (cliRes && cliRes.streamed)),
|
|
769
|
+
streamLastChar,
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
const transcript = loadTranscript(workspaceRoot, String(state.sessionId || ""));
|
|
774
|
+
state.transcriptEvents = transcript.events;
|
|
775
|
+
state.nlMessages = stripSkillBlocksFromMessages(
|
|
776
|
+
transcriptEventsToMessages(transcript.events, { preferArtifact: true }),
|
|
777
|
+
);
|
|
778
|
+
|
|
732
779
|
if (!cliRes || cliRes.ok === false) {
|
|
733
780
|
const errMsg = String((cliRes && cliRes.error) || "");
|
|
734
781
|
if (isCliCancelledError(errMsg) && state.executionState) {
|
|
@@ -746,9 +793,8 @@ async function runNaturalLanguageTask(task = "", state = {}, options = {}) {
|
|
|
746
793
|
streamLastChar,
|
|
747
794
|
};
|
|
748
795
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
}
|
|
796
|
+
// The turn coordinator owns session identity. Providers/runners receive it
|
|
797
|
+
// as correlation metadata but cannot redirect durable writes mid-turn.
|
|
752
798
|
if (cliRes && cliRes.executionState && typeof cliRes.executionState === "object") {
|
|
753
799
|
// Preserve planMode if the runner returned a fresh empty state without it.
|
|
754
800
|
const priorPlanMode = Boolean(state.executionState && state.executionState.planMode);
|
|
@@ -763,18 +809,6 @@ async function runNaturalLanguageTask(task = "", state = {}, options = {}) {
|
|
|
763
809
|
state.executionState.planModeSource = priorSource;
|
|
764
810
|
}
|
|
765
811
|
}
|
|
766
|
-
if (cliRes && Array.isArray(cliRes.messages)) {
|
|
767
|
-
// Sync first so ensureTranscript does not migrate the just-assigned
|
|
768
|
-
// nlMessages and then append the same delta again.
|
|
769
|
-
syncMessagesToTranscript(state, cliRes.messages, workspaceRoot, {
|
|
770
|
-
baselineCount: lastTranscriptBaseline,
|
|
771
|
-
});
|
|
772
|
-
state.nlMessages = stripSkillBlocksFromMessages(
|
|
773
|
-
Array.isArray(state.nlMessages) && state.nlMessages.length > 0
|
|
774
|
-
? state.nlMessages
|
|
775
|
-
: cliRes.messages,
|
|
776
|
-
);
|
|
777
|
-
}
|
|
778
812
|
const normalized = String(cliRes.output || "").trim();
|
|
779
813
|
const sideEffects = parseStructuredSideEffects(normalized);
|
|
780
814
|
if (sideEffects) {
|
|
@@ -1028,6 +1062,9 @@ function resumeSessionState(state = {}, sessionId = "", workspaceRoot = process.
|
|
|
1028
1062
|
state.contextMeter = snapshot.contextMeter && typeof snapshot.contextMeter === "object"
|
|
1029
1063
|
? snapshot.contextMeter
|
|
1030
1064
|
: null;
|
|
1065
|
+
// A resume is a pure projection rebuild. Never reuse events belonging to
|
|
1066
|
+
// the session that happened to be active before /resume.
|
|
1067
|
+
state.transcriptEvents = [];
|
|
1031
1068
|
ensureContextSessionState(state);
|
|
1032
1069
|
const { ensureTranscript } = require("./context/assembler");
|
|
1033
1070
|
ensureTranscript(state, state.workspaceRoot);
|
|
@@ -1051,6 +1088,7 @@ function resumeSessionState(state = {}, sessionId = "", workspaceRoot = process.
|
|
|
1051
1088
|
*/
|
|
1052
1089
|
async function resumeAfterUserInteraction(answerText = "", state = {}, options = {}) {
|
|
1053
1090
|
ensureContextSessionState(state);
|
|
1091
|
+
state.sessionId = resolveSessionId(state.sessionId);
|
|
1054
1092
|
const { resolveUserInteraction } = require("./context/userInteraction");
|
|
1055
1093
|
const { appendAnswerToolResult } = require("./nativeRunner");
|
|
1056
1094
|
const resolved = resolveUserInteraction(state.executionState, answerText);
|
|
@@ -1071,6 +1109,7 @@ async function resumeAfterUserInteraction(answerText = "", state = {}, options =
|
|
|
1071
1109
|
};
|
|
1072
1110
|
|
|
1073
1111
|
let messages = Array.isArray(state.nlMessages) ? state.nlMessages.slice() : [];
|
|
1112
|
+
const inputStart = messages.length;
|
|
1074
1113
|
if (resolved.continueMode === "tool_result" && resolved.resume && resolved.resume.call) {
|
|
1075
1114
|
const appended = appendAnswerToolResult(messages, resolved.resume, resolved.answer);
|
|
1076
1115
|
if (!appended.ok) {
|
|
@@ -1083,9 +1122,28 @@ async function resumeAfterUserInteraction(answerText = "", state = {}, options =
|
|
|
1083
1122
|
content: JSON.stringify(resolved.answer),
|
|
1084
1123
|
});
|
|
1085
1124
|
}
|
|
1086
|
-
state.nlMessages = messages;
|
|
1087
|
-
|
|
1088
1125
|
const workspaceRoot = state.workspaceRoot || process.cwd();
|
|
1126
|
+
const turnId = `turn_${Date.now().toString(36)}_${randomUUID().slice(0, 8)}`;
|
|
1127
|
+
const interactionItems = messages.slice(inputStart);
|
|
1128
|
+
const interactionAppend = appendTurnMessages(
|
|
1129
|
+
workspaceRoot,
|
|
1130
|
+
String(state.sessionId || ""),
|
|
1131
|
+
turnId,
|
|
1132
|
+
interactionItems,
|
|
1133
|
+
{ scope: "interaction" },
|
|
1134
|
+
);
|
|
1135
|
+
if (!interactionAppend.ok) {
|
|
1136
|
+
return {
|
|
1137
|
+
ok: false,
|
|
1138
|
+
error: `failed to persist interaction answer: ${interactionAppend.error}`,
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
let transcript = loadTranscript(workspaceRoot, String(state.sessionId || ""));
|
|
1142
|
+
state.transcriptEvents = transcript.events;
|
|
1143
|
+
state.nlMessages = stripSkillBlocksFromMessages(
|
|
1144
|
+
transcriptEventsToMessages(transcript.events, { preferArtifact: true }),
|
|
1145
|
+
);
|
|
1146
|
+
|
|
1089
1147
|
const assembled = assembleModelContext(state, {
|
|
1090
1148
|
workspaceRoot,
|
|
1091
1149
|
provider: state.provider,
|
|
@@ -1131,9 +1189,30 @@ async function resumeAfterUserInteraction(answerText = "", state = {}, options =
|
|
|
1131
1189
|
if (cliRes && cliRes.executionState) {
|
|
1132
1190
|
state.executionState = cliRes.executionState;
|
|
1133
1191
|
}
|
|
1134
|
-
if (cliRes && Array.isArray(cliRes.
|
|
1135
|
-
|
|
1192
|
+
if (cliRes && Array.isArray(cliRes.turnItems) && cliRes.turnItems.length > 0) {
|
|
1193
|
+
const outputAppend = appendTurnMessages(
|
|
1194
|
+
workspaceRoot,
|
|
1195
|
+
String(state.sessionId || ""),
|
|
1196
|
+
turnId,
|
|
1197
|
+
cliRes.turnItems,
|
|
1198
|
+
{ scope: "output" },
|
|
1199
|
+
);
|
|
1200
|
+
if (!outputAppend.ok) {
|
|
1201
|
+
return {
|
|
1202
|
+
ok: false,
|
|
1203
|
+
error: `failed to persist interaction output: ${outputAppend.error}`,
|
|
1204
|
+
logs,
|
|
1205
|
+
waitingUserInteraction: false,
|
|
1206
|
+
streamed: Boolean(cliRes.streamed),
|
|
1207
|
+
streamLastChar,
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1136
1210
|
}
|
|
1211
|
+
transcript = loadTranscript(workspaceRoot, String(state.sessionId || ""));
|
|
1212
|
+
state.transcriptEvents = transcript.events;
|
|
1213
|
+
state.nlMessages = stripSkillBlocksFromMessages(
|
|
1214
|
+
transcriptEventsToMessages(transcript.events, { preferArtifact: true }),
|
|
1215
|
+
);
|
|
1137
1216
|
if (cliRes && cliRes.contextMeter) {
|
|
1138
1217
|
state.contextMeter = cliRes.contextMeter;
|
|
1139
1218
|
}
|
|
@@ -5,7 +5,6 @@ const {
|
|
|
5
5
|
transcriptEventsToMessages,
|
|
6
6
|
migrateNlMessagesToTranscript,
|
|
7
7
|
} = require("./transcript");
|
|
8
|
-
const { appendTranscriptMessagesForStorage } = require("./transcriptSync");
|
|
9
8
|
const { reduceToolResult } = require("./reducers");
|
|
10
9
|
const { saveArtifact } = require("./artifacts");
|
|
11
10
|
const { buildLayeredSystemPrompt } = require("./promptLayers");
|
|
@@ -551,104 +550,6 @@ function recordToolCallInSession(session = {}, persisted = {}, workspaceRoot = p
|
|
|
551
550
|
}
|
|
552
551
|
}
|
|
553
552
|
|
|
554
|
-
function syncMessagesToTranscript(session = {}, messages = [], workspaceRoot = process.cwd(), options = {}) {
|
|
555
|
-
const sessionId = String(session.sessionId || "").trim();
|
|
556
|
-
if (!sessionId) return [];
|
|
557
|
-
const prior = ensureTranscript(session, workspaceRoot);
|
|
558
|
-
const full = Array.isArray(messages) ? messages : [];
|
|
559
|
-
if (full.length === 0) return prior;
|
|
560
|
-
|
|
561
|
-
// `messages` is often a WINDOWED model view (plus this turn's delta), not the
|
|
562
|
-
// full transcript. Comparing lengths to prior transcript events dropped every
|
|
563
|
-
// new user turn once the transcript grew past the window — resume then lost
|
|
564
|
-
// green › user rows. Prefer an explicit baseline, else suffix/prefix match.
|
|
565
|
-
let baseline = Number.isFinite(options.baselineCount)
|
|
566
|
-
? Math.max(0, Math.floor(options.baselineCount))
|
|
567
|
-
: null;
|
|
568
|
-
if (baseline == null) {
|
|
569
|
-
const existingMessages = transcriptEventsToMessages(prior, {
|
|
570
|
-
preferArtifact: true,
|
|
571
|
-
});
|
|
572
|
-
baseline = matchTranscriptBaseline(existingMessages, full);
|
|
573
|
-
}
|
|
574
|
-
baseline = Math.max(0, Math.min(full.length, baseline));
|
|
575
|
-
|
|
576
|
-
if (full.length <= baseline) {
|
|
577
|
-
session.nlMessages = transcriptEventsToMessages(session.transcriptEvents, {
|
|
578
|
-
preferArtifact: true,
|
|
579
|
-
});
|
|
580
|
-
return session.transcriptEvents || prior;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
const delta = full.slice(baseline);
|
|
584
|
-
const extra = {
|
|
585
|
-
segmentId: session.executionState && session.executionState.currentSegmentId
|
|
586
|
-
? session.executionState.currentSegmentId
|
|
587
|
-
: "",
|
|
588
|
-
};
|
|
589
|
-
appendTranscriptMessagesForStorage(workspaceRoot, sessionId, delta, extra);
|
|
590
|
-
session.transcriptEvents = loadTranscript(workspaceRoot, sessionId).events;
|
|
591
|
-
session.nlMessages = transcriptEventsToMessages(session.transcriptEvents, {
|
|
592
|
-
preferArtifact: true,
|
|
593
|
-
});
|
|
594
|
-
session.summary = buildRollingSummary(session.transcriptEvents, session.summary, session);
|
|
595
|
-
return session.transcriptEvents;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
/**
|
|
599
|
-
* Fingerprint a chat message for transcript alignment. Tool payloads are
|
|
600
|
-
* compared by call id (content is often artifact-compressed on disk).
|
|
601
|
-
*/
|
|
602
|
-
function messageSyncFingerprint(message = {}) {
|
|
603
|
-
if (!message || typeof message !== "object") return "";
|
|
604
|
-
const role = String(message.role || "").trim().toLowerCase();
|
|
605
|
-
if (role === "tool") {
|
|
606
|
-
return `tool|${String(message.tool_call_id || "").trim()}`;
|
|
607
|
-
}
|
|
608
|
-
if (role === "assistant" && Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
|
|
609
|
-
const ids = message.tool_calls
|
|
610
|
-
.map((call) => String((call && call.id) || "").trim())
|
|
611
|
-
.filter(Boolean)
|
|
612
|
-
.join(",");
|
|
613
|
-
return `assistant_tools|${ids}`;
|
|
614
|
-
}
|
|
615
|
-
let content = "";
|
|
616
|
-
if (typeof message.content === "string") content = message.content;
|
|
617
|
-
else if (message.content != null) {
|
|
618
|
-
try {
|
|
619
|
-
content = JSON.stringify(message.content);
|
|
620
|
-
} catch {
|
|
621
|
-
content = String(message.content);
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
const compact = content.replace(/\s+/g, " ").trim();
|
|
625
|
-
return `${role}|${compact.length}|${compact.slice(0, 240)}`;
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
/**
|
|
629
|
-
* How much of `next` is already covered by the end of `existing`.
|
|
630
|
-
* Returns the length of the matched prefix of `next` (baseline for slicing).
|
|
631
|
-
*/
|
|
632
|
-
function matchTranscriptBaseline(existing = [], next = []) {
|
|
633
|
-
const prior = Array.isArray(existing) ? existing : [];
|
|
634
|
-
const incoming = Array.isArray(next) ? next : [];
|
|
635
|
-
if (incoming.length === 0) return 0;
|
|
636
|
-
const priorFingerprints = prior.map(messageSyncFingerprint);
|
|
637
|
-
const nextFingerprints = incoming.map(messageSyncFingerprint);
|
|
638
|
-
const max = Math.min(priorFingerprints.length, nextFingerprints.length);
|
|
639
|
-
for (let k = max; k >= 0; k -= 1) {
|
|
640
|
-
let matched = true;
|
|
641
|
-
for (let i = 0; i < k; i += 1) {
|
|
642
|
-
if (priorFingerprints[priorFingerprints.length - k + i] !== nextFingerprints[i]) {
|
|
643
|
-
matched = false;
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
}
|
|
647
|
-
if (matched) return k;
|
|
648
|
-
}
|
|
649
|
-
return 0;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
553
|
function applyContextSideEffects(session = {}, sideEffects = {}, workspaceRoot = process.cwd()) {
|
|
653
554
|
const effects = sideEffects && typeof sideEffects === "object" ? sideEffects : {};
|
|
654
555
|
if (effects.stateCommit) {
|
|
@@ -710,10 +611,7 @@ module.exports = {
|
|
|
710
611
|
assembleModelContext,
|
|
711
612
|
persistToolResultToContext,
|
|
712
613
|
recordToolCallInSession,
|
|
713
|
-
syncMessagesToTranscript,
|
|
714
614
|
applyContextSideEffects,
|
|
715
615
|
commitAfterSegmentEnd,
|
|
716
616
|
ensureProjectSnapshot,
|
|
717
|
-
messageSyncFingerprint,
|
|
718
|
-
matchTranscriptBaseline,
|
|
719
617
|
};
|
|
@@ -74,6 +74,11 @@ function readTranscriptFile(filePath = "") {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
function loadTranscript(workspaceRoot = process.cwd(), sessionId = "") {
|
|
77
|
+
const { loadTranscriptProjection } = require("../conversation/sessionJournal");
|
|
78
|
+
const projected = loadTranscriptProjection(workspaceRoot, sessionId);
|
|
79
|
+
if (projected.source === "journal-v3" || projected.events.length > 0) {
|
|
80
|
+
return projected;
|
|
81
|
+
}
|
|
77
82
|
const filePath = getTranscriptFilePath(workspaceRoot, sessionId);
|
|
78
83
|
return {
|
|
79
84
|
filePath,
|