u-foo 3.0.0 → 3.0.2
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/package.json +1 -1
- package/src/agents/prompts/native/tasks.js +4 -1
- package/src/app/chat/commandExecutor.js +111 -1
- package/src/app/chat/commands.js +2 -1
- package/src/app/chat/daemonMessageRouter.js +1 -1
- package/src/app/chat/inputSubmitHandler.js +3 -2
- package/src/code/agent.js +17 -3
- package/src/code/commands.js +3 -3
- package/src/code/context/executionSegment.js +5 -0
- package/src/code/context/planMode.js +8 -1
- package/src/code/context/promptLayers.js +10 -9
- package/src/code/dispatch.js +4 -0
- package/src/code/index.js +2 -0
- package/src/code/modelCommand.js +199 -23
- package/src/code/nativeRunner.js +299 -225
- package/src/code/protocol/controlPlane.js +93 -0
- package/src/code/protocol/faultHarness.js +90 -0
- package/src/code/protocol/index.js +20 -0
- package/src/code/protocol/loopEvents.js +102 -0
- package/src/code/protocol/materialize.js +107 -0
- package/src/code/protocol/messageFixtures.js +116 -0
- package/src/code/protocol/ownership.js +147 -0
- package/src/code/protocol/protocolValidator.js +165 -0
- package/src/code/protocol/suspension.js +173 -0
- package/src/code/protocol/toolCallLedger.js +222 -0
- package/src/code/protocol/transitions.js +97 -0
- package/src/code/providers/anthropicMessagesTransport.js +93 -0
- package/src/code/providers/index.js +8 -0
- package/src/code/providers/modelsCatalog.js +304 -0
- package/src/code/providers/openaiChatTransport.js +98 -0
- package/src/code/providers/transportContract.js +46 -0
- package/src/code/repl.js +45 -29
- package/src/code/runtime/taskControl.js +177 -53
- package/src/code/runtime/taskFocus.js +30 -10
- package/src/code/runtime/taskLoop.js +25 -3
- package/src/code/runtime/taskRun.js +172 -2
- package/src/code/runtime/workspaceLease.js +41 -0
- package/src/code/sessionStore.js +1 -0
- package/src/code/taskRoute.js +73 -0
- package/src/code/thinkingLevels.js +132 -0
- package/src/code/tools/taskRun.js +118 -0
- package/src/config.js +10 -1
- package/src/ui/format/index.js +48 -3
- package/src/ui/ink/ChatApp.js +137 -25
- package/src/ui/ink/UcodeApp.js +38 -30
- package/src/ui/ink/chatLogModel.js +238 -32
- package/src/ui/ink/chatReducer.js +18 -6
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const { renderMarkdownLinesAnsi } = require("../format/markdownRenderer");
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Match ucode: markdown only for conversational prose. System/user rows stay
|
|
6
|
+
// plain so app-generated prefixes (›, ✓, Error:) keep Ink color control.
|
|
7
|
+
const MARKDOWN_BODY_KINDS = new Set(["assistant", "agent", "report"]);
|
|
6
8
|
|
|
7
9
|
function stripBlessedTags(text = "") {
|
|
8
10
|
return String(text || "")
|
|
@@ -30,6 +32,10 @@ function compactDividerLabel(text = "") {
|
|
|
30
32
|
return label || String(text || "").trim() || "section";
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
function stripUserPromptPrefix(text = "") {
|
|
36
|
+
return String(text || "").replace(/^›\s*/, "");
|
|
37
|
+
}
|
|
38
|
+
|
|
33
39
|
function splitSpeakerBody(raw = "") {
|
|
34
40
|
const source = String(raw || "");
|
|
35
41
|
const dotIdx = source.indexOf(" · ");
|
|
@@ -48,13 +54,16 @@ function splitSpeakerBody(raw = "") {
|
|
|
48
54
|
|
|
49
55
|
function formatChatLogBody(body = "", kind = "plain", markdownState = null) {
|
|
50
56
|
const text = String(body || "");
|
|
51
|
-
|
|
57
|
+
const state = markdownState && typeof markdownState === "object"
|
|
58
|
+
? markdownState
|
|
59
|
+
: { inCodeBlock: false };
|
|
60
|
+
// Continuations inside an open fence stay plain-kind but still need the
|
|
61
|
+
// shared ANSI renderer so the fence closes cleanly (same as ucode).
|
|
62
|
+
const allowMarkdown = MARKDOWN_BODY_KINDS.has(kind) || state.inCodeBlock === true;
|
|
63
|
+
if (!allowMarkdown) return text;
|
|
52
64
|
// Structural markers / empty rows stay untouched.
|
|
53
|
-
if (!text.trim()) return text;
|
|
65
|
+
if (!text.trim() && !state.inCodeBlock) return text;
|
|
54
66
|
try {
|
|
55
|
-
const state = markdownState && typeof markdownState === "object"
|
|
56
|
-
? markdownState
|
|
57
|
-
: { inCodeBlock: false };
|
|
58
67
|
const lines = renderMarkdownLinesAnsi(text, state);
|
|
59
68
|
if (!Array.isArray(lines) || lines.length === 0) return text;
|
|
60
69
|
return lines.length === 1 ? lines[0] : lines.join("\n");
|
|
@@ -95,6 +104,15 @@ function classifyChatLogLine(text = "") {
|
|
|
95
104
|
body: rawBody || clean.replace(/^[✓✔]\s*/, ""),
|
|
96
105
|
};
|
|
97
106
|
}
|
|
107
|
+
// ucode-style user prompt already embedded in text
|
|
108
|
+
if (/^›\s/.test(raw) || raw === "›") {
|
|
109
|
+
return {
|
|
110
|
+
kind: "user",
|
|
111
|
+
marker: "›",
|
|
112
|
+
speaker: "",
|
|
113
|
+
body: stripUserPromptPrefix(raw) || " ",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
98
116
|
const cleanDot = clean.match(/^([^·\n]{1,64})\s+·\s+(.*)$/);
|
|
99
117
|
if (cleanDot) {
|
|
100
118
|
const parts = splitSpeakerBody(raw);
|
|
@@ -103,7 +121,7 @@ function classifyChatLogLine(text = "") {
|
|
|
103
121
|
const kind = lower === "ufoo" ? "assistant" : "agent";
|
|
104
122
|
return {
|
|
105
123
|
kind,
|
|
106
|
-
marker: kind === "assistant" ? "◆" : "
|
|
124
|
+
marker: kind === "assistant" ? "◆" : "◇",
|
|
107
125
|
speaker,
|
|
108
126
|
body: parts.body != null ? parts.body : (cleanDot[2] || " "),
|
|
109
127
|
};
|
|
@@ -113,7 +131,7 @@ function classifyChatLogLine(text = "") {
|
|
|
113
131
|
const parts = splitSpeakerBody(raw);
|
|
114
132
|
return {
|
|
115
133
|
kind: "agent",
|
|
116
|
-
marker: "
|
|
134
|
+
marker: "◇",
|
|
117
135
|
speaker: stripMarkdownDecorators(parts.speaker || cleanColon[1]).trim(),
|
|
118
136
|
body: parts.body != null ? parts.body : (cleanColon[2] || " "),
|
|
119
137
|
};
|
|
@@ -121,30 +139,178 @@ function classifyChatLogLine(text = "") {
|
|
|
121
139
|
return { kind: "plain", marker: "", speaker: "", body: raw || clean };
|
|
122
140
|
}
|
|
123
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Map router/history sourceType onto a display row. Heuristic classification
|
|
144
|
+
* still runs first so speaker/body parsing stays shared; sourceType only
|
|
145
|
+
* upgrades or specializes the visual role (user / system / report / …).
|
|
146
|
+
*/
|
|
147
|
+
function applySourceTypeToRow(row, sourceType = "", meta = {}) {
|
|
148
|
+
const base = row && typeof row === "object" ? { ...row } : classifyChatLogLine("");
|
|
149
|
+
const type = String(sourceType || "").toLowerCase();
|
|
150
|
+
const event = String((meta && (meta.event || meta.Event)) || "").toLowerCase();
|
|
151
|
+
const isReport = type === "report" || event === "controller_report";
|
|
152
|
+
|
|
153
|
+
if (type === "user") {
|
|
154
|
+
if (base.kind === "spacer") {
|
|
155
|
+
return {
|
|
156
|
+
...base,
|
|
157
|
+
kind: "user",
|
|
158
|
+
marker: " ",
|
|
159
|
+
speaker: "",
|
|
160
|
+
body: " ",
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
// Indented continuations of a multi-line user echo stay user-colored
|
|
164
|
+
// without a second › marker.
|
|
165
|
+
if (base.kind === "plain" && /^\s{2,}\S/.test(String(base.body || ""))) {
|
|
166
|
+
return {
|
|
167
|
+
...base,
|
|
168
|
+
kind: "user",
|
|
169
|
+
marker: " ",
|
|
170
|
+
speaker: "",
|
|
171
|
+
body: base.body || " ",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
...base,
|
|
176
|
+
kind: "user",
|
|
177
|
+
marker: "›",
|
|
178
|
+
speaker: "",
|
|
179
|
+
body: stripUserPromptPrefix(base.body || ""),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
if (type === "error") {
|
|
183
|
+
if (base.kind === "plain" || base.kind === "spacer") return base;
|
|
184
|
+
return {
|
|
185
|
+
...base,
|
|
186
|
+
kind: "error",
|
|
187
|
+
marker: base.marker === "!" ? base.marker : "!",
|
|
188
|
+
speaker: base.speaker || "error",
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (isReport) {
|
|
192
|
+
// Keep indented continuations foldable under the report head.
|
|
193
|
+
if (base.kind === "plain" || base.kind === "spacer") return base;
|
|
194
|
+
const speaker = base.speaker || "report";
|
|
195
|
+
return {
|
|
196
|
+
...base,
|
|
197
|
+
kind: "report",
|
|
198
|
+
marker: "▣",
|
|
199
|
+
speaker,
|
|
200
|
+
body: base.body || " ",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (type === "bus") {
|
|
204
|
+
if (base.kind === "plain" || base.kind === "spacer" || base.kind === "success" || base.kind === "error") {
|
|
205
|
+
return base;
|
|
206
|
+
}
|
|
207
|
+
if (base.kind === "assistant") return base;
|
|
208
|
+
return {
|
|
209
|
+
...base,
|
|
210
|
+
kind: "agent",
|
|
211
|
+
marker: base.marker === "◆" ? "◆" : "◇",
|
|
212
|
+
speaker: base.speaker || "bus",
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (type === "reply") {
|
|
216
|
+
if (base.kind === "plain" || base.kind === "spacer") return base;
|
|
217
|
+
return {
|
|
218
|
+
...base,
|
|
219
|
+
kind: "assistant",
|
|
220
|
+
marker: "◆",
|
|
221
|
+
speaker: base.speaker || "ufoo",
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
if (type === "system" || type === "disambiguate") {
|
|
225
|
+
// Keep success/error markers when the line already carries them.
|
|
226
|
+
if (base.kind === "success" || base.kind === "error" || base.kind === "divider" || base.kind === "banner") {
|
|
227
|
+
return base;
|
|
228
|
+
}
|
|
229
|
+
if (base.kind === "spacer") return base;
|
|
230
|
+
|
|
231
|
+
const body = String(base.body || "");
|
|
232
|
+
// Config / list detail rows already ship their own " • …" prefix from
|
|
233
|
+
// commandExecutor. Keep them plain so they fold under the ✓ header and
|
|
234
|
+
// don't pick up a competing tiny "·" gutter glyph.
|
|
235
|
+
if (/^\s*[•·\-*]/.test(body) || /^\s{2,}\S/.test(body)) {
|
|
236
|
+
return {
|
|
237
|
+
...base,
|
|
238
|
+
kind: "plain",
|
|
239
|
+
marker: "",
|
|
240
|
+
speaker: "",
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Bare system notices: dim text, no extra marker competing with ›/◆/◇/✓.
|
|
245
|
+
return {
|
|
246
|
+
...base,
|
|
247
|
+
kind: "system",
|
|
248
|
+
marker: " ",
|
|
249
|
+
speaker: "",
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
return base;
|
|
253
|
+
}
|
|
254
|
+
|
|
124
255
|
function defaultMarkerForKind(kind = "", speaker = "") {
|
|
256
|
+
if (kind === "user") return "›";
|
|
125
257
|
if (kind === "assistant") return "◆";
|
|
126
|
-
if (kind === "agent") return "
|
|
258
|
+
if (kind === "agent") return "◇";
|
|
259
|
+
if (kind === "report") return "▣";
|
|
127
260
|
if (kind === "error") return "!";
|
|
128
261
|
if (kind === "success") return "✓";
|
|
129
262
|
if (kind === "divider") return "─";
|
|
130
|
-
if (kind === "meta") return "·";
|
|
263
|
+
if (kind === "meta" || kind === "system") return "·";
|
|
131
264
|
if (kind === "banner" || kind === "spacer") return " ";
|
|
132
|
-
return speaker ? "
|
|
265
|
+
return speaker ? "◇" : "";
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function markerTextForRow(kind, marker, speaker) {
|
|
269
|
+
if (kind === "user") return marker === "›" ? "› " : " ";
|
|
270
|
+
if (speaker) return `${marker || " "} `;
|
|
271
|
+
if (kind === "system") return marker && marker !== " " && marker !== "·" ? `${marker} ` : " ";
|
|
272
|
+
return `${marker || " "} `;
|
|
133
273
|
}
|
|
134
274
|
|
|
135
275
|
function buildChatLogLineModel(input = "", options = {}) {
|
|
136
276
|
const markdownState = options && options.markdownState;
|
|
277
|
+
const optionSourceType = options && options.sourceType != null ? options.sourceType : "";
|
|
278
|
+
const optionMeta = options && options.meta && typeof options.meta === "object" ? options.meta : {};
|
|
137
279
|
|
|
138
|
-
if (input && typeof input === "object" && !input.kind) {
|
|
280
|
+
if (input && typeof input === "object" && !input.kind && input.text == null && input.body == null) {
|
|
139
281
|
return buildChatLogLineModel(chatLogEntryText(input), options);
|
|
140
282
|
}
|
|
141
283
|
|
|
142
|
-
if (input && typeof input === "object" && input.kind) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
284
|
+
if (input && typeof input === "object" && (input.kind || input.text != null || input.sourceType || input.type)) {
|
|
285
|
+
const sourceType = String(optionSourceType || input.sourceType || input.type || "");
|
|
286
|
+
const meta = input.meta && typeof input.meta === "object" && !Array.isArray(input.meta)
|
|
287
|
+
? { ...optionMeta, ...input.meta }
|
|
288
|
+
: optionMeta;
|
|
289
|
+
const text = input.text != null ? String(input.text) : chatLogEntryText(input);
|
|
290
|
+
|
|
291
|
+
// Prefer re-deriving from text + sourceType when markdown fence state must
|
|
292
|
+
// advance across lines; otherwise reuse a pre-classified object.
|
|
293
|
+
if (!input.kind || sourceType || markdownState) {
|
|
294
|
+
const classified = applySourceTypeToRow(classifyChatLogLine(text), sourceType, meta);
|
|
295
|
+
const kind = classified.kind;
|
|
296
|
+
const speaker = classified.speaker;
|
|
297
|
+
const marker = classified.marker != null ? classified.marker : defaultMarkerForKind(kind, speaker);
|
|
298
|
+
const rawBody = kind === "user"
|
|
299
|
+
? stripUserPromptPrefix(classified.body || " ")
|
|
300
|
+
: kind === "plain"
|
|
301
|
+
? compactContinuationIndent(classified.body || " ")
|
|
302
|
+
: (classified.body || " ");
|
|
303
|
+
const bodyText = formatChatLogBody(rawBody, kind, markdownState || { inCodeBlock: false });
|
|
304
|
+
return {
|
|
305
|
+
kind,
|
|
306
|
+
marker,
|
|
307
|
+
speaker,
|
|
308
|
+
body: classified.body,
|
|
309
|
+
markerText: markerTextForRow(kind, marker, speaker),
|
|
310
|
+
bodyText,
|
|
311
|
+
};
|
|
147
312
|
}
|
|
313
|
+
|
|
148
314
|
const kind = String(input.kind || "plain");
|
|
149
315
|
const speaker = String(input.speaker || "");
|
|
150
316
|
const marker = input.marker != null ? String(input.marker) : defaultMarkerForKind(kind, speaker);
|
|
@@ -162,40 +328,64 @@ function buildChatLogLineModel(input = "", options = {}) {
|
|
|
162
328
|
body: input.body != null ? String(input.body) : rawBody,
|
|
163
329
|
markerText: input.markerText != null
|
|
164
330
|
? String(input.markerText)
|
|
165
|
-
: (
|
|
331
|
+
: markerTextForRow(kind, marker, speaker),
|
|
166
332
|
bodyText,
|
|
167
333
|
};
|
|
168
334
|
}
|
|
169
335
|
|
|
170
|
-
const
|
|
171
|
-
const
|
|
336
|
+
const sourceType = String(optionSourceType || "");
|
|
337
|
+
const row = applySourceTypeToRow(classifyChatLogLine(input), sourceType, optionMeta);
|
|
172
338
|
const body = row.kind === "plain"
|
|
173
339
|
? compactContinuationIndent(row.body || " ")
|
|
174
|
-
:
|
|
340
|
+
: row.kind === "user"
|
|
341
|
+
? stripUserPromptPrefix(row.body || " ")
|
|
342
|
+
: (row.body || " ");
|
|
175
343
|
return {
|
|
176
344
|
...row,
|
|
177
|
-
markerText:
|
|
345
|
+
markerText: markerTextForRow(row.kind, row.marker, row.speaker),
|
|
178
346
|
bodyText: formatChatLogBody(body, row.kind, markdownState || { inCodeBlock: false }),
|
|
179
347
|
};
|
|
180
348
|
}
|
|
181
349
|
|
|
182
350
|
function normalizeEntryInput(input) {
|
|
183
|
-
if (input
|
|
184
|
-
|
|
351
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
352
|
+
return { text: input };
|
|
353
|
+
}
|
|
354
|
+
// Defend against nested payloads like `{ text: { text, sourceType } }`
|
|
355
|
+
// (e.g. history objects fed through createInitialState as `text: line`).
|
|
356
|
+
let text = input.text;
|
|
357
|
+
let sourceType = input.sourceType || input.type || "";
|
|
358
|
+
let meta = input.meta;
|
|
359
|
+
if (text && typeof text === "object" && !Array.isArray(text)) {
|
|
360
|
+
sourceType = sourceType || text.sourceType || text.type || "";
|
|
361
|
+
if (!meta && text.meta && typeof text.meta === "object") meta = text.meta;
|
|
362
|
+
text = text.text != null ? text.text : chatLogEntryText(text);
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
...input,
|
|
366
|
+
text: text != null ? text : "",
|
|
367
|
+
sourceType,
|
|
368
|
+
type: sourceType || input.type || "",
|
|
369
|
+
meta: meta && typeof meta === "object" && !Array.isArray(meta) ? meta : {},
|
|
370
|
+
};
|
|
185
371
|
}
|
|
186
372
|
|
|
187
373
|
function createChatLogEntry(input = "", id = "", options = {}) {
|
|
188
374
|
const source = normalizeEntryInput(input);
|
|
189
|
-
const text = String(source.text != null ? source.text :
|
|
375
|
+
const text = String(source.text != null ? source.text : "");
|
|
190
376
|
const markdownState = options && options.markdownState
|
|
191
377
|
? options.markdownState
|
|
192
378
|
: { inCodeBlock: false };
|
|
193
|
-
const row = source.kind && !(options && options.markdownState)
|
|
194
|
-
? buildChatLogLineModel({ ...source, text }, { markdownState })
|
|
195
|
-
: buildChatLogLineModel(text, { markdownState });
|
|
196
379
|
const meta = source.meta && typeof source.meta === "object" && !Array.isArray(source.meta)
|
|
197
380
|
? { ...source.meta }
|
|
198
381
|
: {};
|
|
382
|
+
const sourceType = String(source.sourceType || source.type || options.sourceType || "");
|
|
383
|
+
const row = buildChatLogLineModel({
|
|
384
|
+
...source,
|
|
385
|
+
text,
|
|
386
|
+
sourceType,
|
|
387
|
+
meta,
|
|
388
|
+
}, { markdownState, sourceType, meta });
|
|
199
389
|
const entry = {
|
|
200
390
|
id: String(id || source.id || ""),
|
|
201
391
|
text,
|
|
@@ -205,7 +395,7 @@ function createChatLogEntry(input = "", id = "", options = {}) {
|
|
|
205
395
|
body: row.body,
|
|
206
396
|
markerText: row.markerText,
|
|
207
397
|
bodyText: row.bodyText,
|
|
208
|
-
sourceType
|
|
398
|
+
sourceType,
|
|
209
399
|
meta,
|
|
210
400
|
};
|
|
211
401
|
return entry;
|
|
@@ -215,18 +405,25 @@ function chatLogEntryText(entry = "") {
|
|
|
215
405
|
if (typeof entry === "string") return entry;
|
|
216
406
|
if (!entry || typeof entry !== "object") return "";
|
|
217
407
|
if (entry.text != null) return String(entry.text);
|
|
408
|
+
if (entry.kind === "user") {
|
|
409
|
+
const body = stripUserPromptPrefix(entry.bodyText || entry.body || "");
|
|
410
|
+
return body ? `› ${body}` : "›";
|
|
411
|
+
}
|
|
218
412
|
if (entry.speaker) return `${entry.speaker} · ${entry.bodyText || entry.body || ""}`;
|
|
219
413
|
return String(entry.bodyText || entry.body || "");
|
|
220
414
|
}
|
|
221
415
|
|
|
222
416
|
function canAppendToChatLogGroup(group, row) {
|
|
223
417
|
if (!group || !row) return false;
|
|
418
|
+
if (group.kind === "user" && row.kind === "user" && row.marker !== "›") return true;
|
|
224
419
|
if (row.kind !== "plain" && row.kind !== "spacer") return false;
|
|
225
420
|
return group.kind === "assistant"
|
|
226
421
|
|| group.kind === "agent"
|
|
422
|
+
|| group.kind === "report"
|
|
227
423
|
|| group.kind === "success"
|
|
228
424
|
|| group.kind === "error"
|
|
229
425
|
|| group.kind === "meta"
|
|
426
|
+
|| group.kind === "system"
|
|
230
427
|
|| group.kind === "plain";
|
|
231
428
|
}
|
|
232
429
|
|
|
@@ -241,13 +438,20 @@ function buildChatLogGroups(items = []) {
|
|
|
241
438
|
const text = item && typeof item === "object" && item.text != null
|
|
242
439
|
? String(item.text)
|
|
243
440
|
: chatLogEntryText(item);
|
|
244
|
-
const
|
|
441
|
+
const sourceType = item && typeof item === "object" ? String(item.sourceType || item.type || "") : "";
|
|
442
|
+
const meta = item && typeof item === "object" && item.meta ? item.meta : {};
|
|
443
|
+
const row = buildChatLogLineModel(
|
|
444
|
+
item && typeof item === "object"
|
|
445
|
+
? { ...item, text, sourceType, meta }
|
|
446
|
+
: text,
|
|
447
|
+
{ markdownState, sourceType, meta }
|
|
448
|
+
);
|
|
245
449
|
const entry = {
|
|
246
450
|
id: itemId,
|
|
247
451
|
text,
|
|
248
452
|
row,
|
|
249
|
-
sourceType
|
|
250
|
-
meta
|
|
453
|
+
sourceType,
|
|
454
|
+
meta,
|
|
251
455
|
continuation: false,
|
|
252
456
|
};
|
|
253
457
|
|
|
@@ -270,9 +474,11 @@ function buildChatLogGroups(items = []) {
|
|
|
270
474
|
module.exports = {
|
|
271
475
|
stripBlessedTags,
|
|
272
476
|
stripMarkdownDecorators,
|
|
477
|
+
stripUserPromptPrefix,
|
|
273
478
|
compactContinuationIndent,
|
|
274
479
|
compactDividerLabel,
|
|
275
480
|
classifyChatLogLine,
|
|
481
|
+
applySourceTypeToRow,
|
|
276
482
|
buildChatLogLineModel,
|
|
277
483
|
formatChatLogBody,
|
|
278
484
|
createChatLogEntry,
|
|
@@ -101,12 +101,20 @@ function createInitialState({ banner = [], globalMode = false, globalScope = "co
|
|
|
101
101
|
const initialLaunchMode = settings.launchMode || "auto";
|
|
102
102
|
const initialAgentProvider = settings.agentProvider || "codex-cli";
|
|
103
103
|
const selectedProviderIndex = Math.max(0, DEFAULT_PROVIDER_OPTIONS.findIndex((opt) => opt.value === initialAgentProvider));
|
|
104
|
+
const seed = Array.isArray(banner) ? banner.concat([""]) : [""];
|
|
104
105
|
return {
|
|
105
|
-
logLines:
|
|
106
|
-
text
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
logLines: seed.map((line, idx) => {
|
|
107
|
+
// History reload may pass structured `{ text, sourceType }` rows —
|
|
108
|
+
// don't wrap those objects as `text` (that yields "[object Object]").
|
|
109
|
+
if (line && typeof line === "object" && !Array.isArray(line)) {
|
|
110
|
+
return createChatLogEntry(line, `b-${idx}`);
|
|
111
|
+
}
|
|
112
|
+
return createChatLogEntry({
|
|
113
|
+
text: line,
|
|
114
|
+
sourceType: "banner",
|
|
115
|
+
}, `b-${idx}`);
|
|
116
|
+
}),
|
|
117
|
+
lineSeq: seed.length,
|
|
110
118
|
draft: "",
|
|
111
119
|
focusMode: "input",
|
|
112
120
|
dashboardView: globalMode ? "projects" : "agents",
|
|
@@ -446,7 +454,11 @@ function reducer(state, action) {
|
|
|
446
454
|
const annotated = prefix && lines.length > 0
|
|
447
455
|
? [`${prefix}${lines[0]}`, ...lines.slice(1).map((l) => ` ${l}`)]
|
|
448
456
|
: lines;
|
|
449
|
-
const next = appendLog(state, annotated)
|
|
457
|
+
const next = appendLog(state, annotated.map((text) => ({
|
|
458
|
+
text,
|
|
459
|
+
type: "bus",
|
|
460
|
+
sourceType: "bus",
|
|
461
|
+
})));
|
|
450
462
|
return { ...next, activeStream: null };
|
|
451
463
|
}
|
|
452
464
|
case "agentView/enter":
|