@keyou007/agent-bot 0.1.22 → 0.1.24-alpha.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/CHANGELOG.md +13 -0
- package/README.md +26 -2
- package/README.zh.md +26 -2
- package/config.example.yaml +5 -0
- package/dist/cli/controlProtocol.js.map +1 -1
- package/dist/cli/help.js +4 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/taskGroupOptions.js +27 -0
- package/dist/cli/taskGroupOptions.js.map +1 -1
- package/dist/cli.js +21 -1
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexProcessManager.js +19 -3
- package/dist/codex/CodexProcessManager.js.map +1 -1
- package/dist/codex/CodexRuntime.js +92 -9
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/commands/CommandRouter.js +27 -0
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/config/loadConfig.js +4 -0
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/config/schema.js +1 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/feishu/CardRenderer.js +31 -2
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuMessageClient.js +6 -3
- package/dist/feishu/FeishuMessageClient.js.map +1 -1
- package/dist/feishu/MergedForwardMessage.js +17 -7
- package/dist/feishu/MergedForwardMessage.js.map +1 -1
- package/dist/index.js +50 -0
- package/dist/index.js.map +1 -1
- package/dist/local-files/TurnPreviewPage.js +106 -87
- package/dist/local-files/TurnPreviewPage.js.map +1 -1
- package/dist/proxy/ConversationExport.js +41 -0
- package/dist/proxy/ConversationExport.js.map +1 -0
- package/dist/proxy/ProxySessionController.js +118 -7
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +65 -0
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/migrations.js +12 -0
- package/dist/state/migrations.js.map +1 -1
- package/dist/updates/AutomaticUpdatePreparer.js +66 -0
- package/dist/updates/AutomaticUpdatePreparer.js.map +1 -0
- package/dist/updates/DailyUpdateMonitor.js +261 -0
- package/dist/updates/DailyUpdateMonitor.js.map +1 -0
- package/dist/updates/PublishedRelease.js +43 -0
- package/dist/updates/PublishedRelease.js.map +1 -0
- package/dist/updates/types.js +2 -0
- package/dist/updates/types.js.map +1 -0
- package/docs/technical-reference.md +4 -0
- package/docs/technical-reference.zh.md +4 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +11 -0
- package/src/cli/controlProtocol.ts +9 -0
- package/src/cli/help.ts +4 -0
- package/src/cli/taskGroupOptions.ts +33 -3
- package/src/cli.ts +21 -0
- package/src/codex/CodexProcessManager.ts +21 -4
- package/src/codex/CodexRuntime.ts +104 -12
- package/src/commands/CommandRouter.ts +24 -0
- package/src/commands/commandTypes.ts +1 -0
- package/src/config/loadConfig.ts +4 -0
- package/src/config/schema.ts +4 -1
- package/src/feishu/CardRenderer.ts +34 -2
- package/src/feishu/FeishuMessageClient.ts +6 -2
- package/src/feishu/MergedForwardMessage.ts +29 -7
- package/src/feishu/types.ts +1 -1
- package/src/index.ts +49 -0
- package/src/local-files/TurnPreviewPage.ts +105 -88
- package/src/proxy/ConversationExport.ts +43 -0
- package/src/proxy/ProxySessionController.ts +166 -8
- package/src/runtime/types.ts +6 -0
- package/src/state/StateStore.ts +69 -0
- package/src/state/migrations.ts +12 -0
- package/src/updates/AutomaticUpdatePreparer.ts +76 -0
- package/src/updates/DailyUpdateMonitor.ts +252 -0
- package/src/updates/PublishedRelease.ts +44 -0
- package/src/updates/types.ts +19 -0
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import MarkdownIt from "markdown-it";
|
|
3
3
|
import type { ToolState } from "../runtime/types.js";
|
|
4
4
|
import type { FileSummary, TurnActivity, TurnViewState, TurnViewStatus } from "../presentation/turnViewTypes.js";
|
|
5
|
-
import { displayToolCommand, formatShellCommandForDisplay } from "../feishu/CardRenderer.js";
|
|
5
|
+
import { displayFilePath, displayToolCommand, formatShellCommandForDisplay, toolStatusIcon } from "../feishu/CardRenderer.js";
|
|
6
6
|
import { isFileUrl, rewriteMarkdownFileLinks } from "./MarkdownFileLinks.js";
|
|
7
7
|
|
|
8
8
|
const MARKDOWN = new MarkdownIt({
|
|
@@ -164,7 +164,12 @@ export const TURN_PREVIEW_CLIENT_SCRIPT = `(() => {
|
|
|
164
164
|
const duration = hours > 0
|
|
165
165
|
? String(hours).padStart(2, "0") + ":" + String(minutes).padStart(2, "0") + ":" + String(remainder).padStart(2, "0")
|
|
166
166
|
: String(minutes).padStart(2, "0") + ":" + String(remainder).padStart(2, "0");
|
|
167
|
-
|
|
167
|
+
const shortDuration = hours > 0
|
|
168
|
+
? String(hours * 60 + minutes) + ":" + String(remainder).padStart(2, "0")
|
|
169
|
+
: minutes > 0
|
|
170
|
+
? String(minutes) + ":" + String(remainder).padStart(2, "0")
|
|
171
|
+
: remainder > 0 ? String(remainder) + "s" : "";
|
|
172
|
+
element.textContent = element.dataset.shortDuration === "1" ? shortDuration : labels.elapsed + duration;
|
|
168
173
|
});
|
|
169
174
|
};
|
|
170
175
|
updateElapsed();
|
|
@@ -288,7 +293,7 @@ export function renderTurnPreviewSnapshot(
|
|
|
288
293
|
return localFileUrl(path.resolve(state.projectCwd, filePath));
|
|
289
294
|
} : undefined;
|
|
290
295
|
const renderText = (value: string): string => renderMarkdown(value, state.projectCwd, localFileUrl);
|
|
291
|
-
const timeline = renderTimeline(state.activities ?? [], renderText, resolveFileUrl, state.fullToolOutputs, state.fullToolErrors, language);
|
|
296
|
+
const timeline = renderTimeline(state.activities ?? [], renderText, resolveFileUrl, state.fullToolOutputs, state.fullToolErrors, language, state.projectCwd);
|
|
292
297
|
const sections = [
|
|
293
298
|
state.prompt ? renderMessageActivity("prompt", state.prompt, "user prompt", renderText) : "",
|
|
294
299
|
state.plan.length > 0 ? renderPlan(state, language) : "",
|
|
@@ -331,10 +336,11 @@ function renderTimeline(
|
|
|
331
336
|
fullToolOutputs?: Record<string, string>,
|
|
332
337
|
fullToolErrors?: Record<string, string>,
|
|
333
338
|
language: TurnPreviewLanguage = "zh",
|
|
339
|
+
projectCwd?: string,
|
|
334
340
|
): string {
|
|
335
341
|
return activities
|
|
336
342
|
.filter((activity): activity is Exclude<TurnActivity, { kind: "reasoning" }> => activity.kind !== "reasoning")
|
|
337
|
-
.map((activity) => renderActivity(activity, renderText, localFileUrl, fullToolOutputs, fullToolErrors, language))
|
|
343
|
+
.map((activity) => renderActivity(activity, renderText, localFileUrl, fullToolOutputs, fullToolErrors, language, projectCwd))
|
|
338
344
|
.join("");
|
|
339
345
|
}
|
|
340
346
|
|
|
@@ -345,6 +351,7 @@ function renderActivity(
|
|
|
345
351
|
fullToolOutputs?: Record<string, string>,
|
|
346
352
|
fullToolErrors?: Record<string, string>,
|
|
347
353
|
language: TurnPreviewLanguage = "zh",
|
|
354
|
+
projectCwd?: string,
|
|
348
355
|
): string {
|
|
349
356
|
switch (activity.kind) {
|
|
350
357
|
case "assistant":
|
|
@@ -352,7 +359,7 @@ function renderActivity(
|
|
|
352
359
|
case "user":
|
|
353
360
|
return renderMessageActivity(activity.id, activity.text, "user", renderText);
|
|
354
361
|
case "tool":
|
|
355
|
-
return renderToolActivity(activity.id, activity.tool, localFileUrl, fullToolOutputs, fullToolErrors, language);
|
|
362
|
+
return renderToolActivity(activity.id, activity.tool, localFileUrl, fullToolOutputs, fullToolErrors, language, projectCwd);
|
|
356
363
|
}
|
|
357
364
|
}
|
|
358
365
|
|
|
@@ -367,6 +374,7 @@ function renderToolActivity(
|
|
|
367
374
|
fullToolOutputs?: Record<string, string>,
|
|
368
375
|
fullToolErrors?: Record<string, string>,
|
|
369
376
|
language: TurnPreviewLanguage = "zh",
|
|
377
|
+
projectCwd?: string,
|
|
370
378
|
): string {
|
|
371
379
|
const labels = previewLabels(language);
|
|
372
380
|
const command = tool.command ? (displayToolCommand(tool.command) || tool.command.trim()) : undefined;
|
|
@@ -376,29 +384,40 @@ function renderToolActivity(
|
|
|
376
384
|
: command
|
|
377
385
|
? formatToolCommand(command)
|
|
378
386
|
: undefined;
|
|
379
|
-
const
|
|
380
|
-
const title = commandInHeader ? displayCommand! : toolTitle(tool, command, repl, language);
|
|
387
|
+
const title = displayCommand ? summarizeToolCommand(displayCommand) : toolTitle(tool, command, repl, language);
|
|
381
388
|
const output = fullToolOutputs?.[tool.id] ?? tool.output;
|
|
382
389
|
const error = fullToolErrors?.[tool.id] ?? tool.error;
|
|
383
390
|
const sameError = Boolean(output && error && normalizeOutput(output) === normalizeOutput(error));
|
|
391
|
+
const displayOutput = output && repl && !sameError ? formatReplOutput(output) : output;
|
|
392
|
+
const displayError = sameError ? undefined : error;
|
|
384
393
|
const detailParts = [
|
|
385
|
-
displayCommand
|
|
386
|
-
|
|
387
|
-
? renderToolOutput(
|
|
394
|
+
displayCommand ? `<div class="tool-command"><span class="tool-command-prefix" aria-hidden="true">$</span><pre class="command-block${repl ? " repl-code" : ""}">${escapeHtml(displayCommand)}</pre></div>` : "",
|
|
395
|
+
displayOutput
|
|
396
|
+
? renderToolOutput(sameError ? labels.resultError : repl ? labels.result : labels.output, displayOutput, sameError, repl)
|
|
388
397
|
: "",
|
|
389
|
-
|
|
390
|
-
? renderToolOutput(
|
|
398
|
+
displayError
|
|
399
|
+
? renderToolOutput(labels.error, displayError, true, repl)
|
|
391
400
|
: "",
|
|
392
|
-
!output && !error && !tool.files?.length ? renderToolStatusRow(tool, language) : "",
|
|
393
401
|
renderToolImage(tool, localFileUrl, language),
|
|
394
402
|
tool.files?.length
|
|
395
|
-
? `<
|
|
403
|
+
? `<ul class="files" aria-label="${escapeAttribute(labels.files)}">${tool.files.map((file) => renderFileEntry(file, localFileUrl, projectCwd)).join("")}</ul>`
|
|
396
404
|
: "",
|
|
397
405
|
].filter(Boolean).join("");
|
|
398
|
-
const titleMarkup =
|
|
406
|
+
const titleMarkup = displayCommand
|
|
399
407
|
? `<code class="tool-command-title">${escapeHtml(title)}</code>`
|
|
400
408
|
: `<span class="tool-title">${escapeHtml(title)}</span>`;
|
|
401
|
-
|
|
409
|
+
const timing = renderToolTiming(tool, "header");
|
|
410
|
+
const statusLabel = toolStatusLabel(tool.status, language);
|
|
411
|
+
const icon = `<span class="tool-status-icon" role="img" aria-label="${escapeAttribute(statusLabel)}" title="${escapeAttribute(statusLabel)}">${toolStatusIcon(tool.status)}</span>`;
|
|
412
|
+
const status = `<span class="tool-state ${tool.status}">${escapeHtml(statusLabel)}</span>`;
|
|
413
|
+
const header = `<summary class="tool-header">${icon}<span class="tool-summary-content">${titleMarkup}</span><span class="tool-meta">${timing}</span></summary>`;
|
|
414
|
+
const startedAt = tool.startedAt === undefined ? undefined : new Date(tool.startedAt);
|
|
415
|
+
const startTime = startedAt
|
|
416
|
+
? `<time datetime="${startedAt.toISOString()}" title="${escapeAttribute(labels.startingTime)}">${new Intl.DateTimeFormat(language === "zh" ? "zh-CN" : "en-GB", { hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23" }).format(startedAt)}</time>`
|
|
417
|
+
: `<span title="${escapeAttribute(labels.startingTime)}">${escapeHtml(labels.unknown)}</span>`;
|
|
418
|
+
const characterCount = (displayOutput?.length ?? 0) + (displayError?.length ?? 0);
|
|
419
|
+
const footer = `<div class="tool-footer">${status}${startTime}${renderToolTiming(tool, "footer")}<span>${formatNumber(characterCount)} ${escapeHtml(labels.characters)}</span></div>`;
|
|
420
|
+
return `<article class="activity tool"><details class="tool-step" data-tool-status="${tool.status}" data-activity-id="${escapeAttribute(activityId)}">${header}<div class="tool-body"><div class="tool-content" data-scroll-id="${escapeAttribute(`${activityId}:content`)}" tabindex="0">${detailParts || `<div class="muted">${escapeHtml(labels.noToolDetails)}</div>`}</div>${footer}</div></details></article>`;
|
|
402
421
|
}
|
|
403
422
|
|
|
404
423
|
function toolTitle(tool: ToolState, command?: string, repl = false, language: TurnPreviewLanguage = "zh"): string {
|
|
@@ -408,6 +427,12 @@ function toolTitle(tool: ToolState, command?: string, repl = false, language: Tu
|
|
|
408
427
|
return title || tool.kind;
|
|
409
428
|
}
|
|
410
429
|
|
|
430
|
+
function summarizeToolCommand(command: string): string {
|
|
431
|
+
const summary = command.replace(/[\\`^][ \t]*\r?\n/gu, " ").replace(/\s+/gu, " ").trim();
|
|
432
|
+
if (summary.length <= 160) return summary;
|
|
433
|
+
return `${summary.slice(0, 157).trimEnd()}…`;
|
|
434
|
+
}
|
|
435
|
+
|
|
411
436
|
function isReplTool(tool: ToolState): boolean {
|
|
412
437
|
const title = tool.title.trim();
|
|
413
438
|
const titleLooksLikeToolName = title.length <= 120 && !/[\s"'`]/u.test(title);
|
|
@@ -518,36 +543,29 @@ function normalizeReplText(value: string): string {
|
|
|
518
543
|
}
|
|
519
544
|
|
|
520
545
|
function renderToolOutput(
|
|
521
|
-
activityId: string,
|
|
522
|
-
kind: string,
|
|
523
546
|
label: string,
|
|
524
547
|
value: string,
|
|
525
548
|
error = false,
|
|
526
549
|
repl = false,
|
|
527
|
-
language: TurnPreviewLanguage = "zh",
|
|
528
|
-
tool?: ToolState,
|
|
529
550
|
): string {
|
|
530
|
-
|
|
531
|
-
const displayValue = repl && !error ? formatReplOutput(value) : value;
|
|
532
|
-
const labels = previewLabels(language);
|
|
533
|
-
const status = tool?.status ?? "completed";
|
|
534
|
-
const timing = tool ? renderToolTiming(tool, language) : "";
|
|
535
|
-
return `<details class="tool-output${error ? " error-output" : ""}${repl ? " repl-result" : ""}" data-activity-id="${id}"><summary><span class="tool-output-label">${escapeHtml(label)}</span><span class="tool-output-meta">${timing}<span class="tool-state ${status}" aria-label="${escapeAttribute(status)}">${toolStatusIcon(status, language)}</span><span>${formatNumber(displayValue.length)} ${escapeHtml(labels.characters)}</span></span></summary><pre data-scroll-id="${id}">${escapeHtml(displayValue)}</pre></details>`;
|
|
551
|
+
return `<pre class="tool-output${error ? " error-output" : ""}${repl ? " repl-result" : ""}" aria-label="${escapeAttribute(label)}">${escapeHtml(value)}</pre>`;
|
|
536
552
|
}
|
|
537
553
|
|
|
538
|
-
function
|
|
539
|
-
const
|
|
540
|
-
|
|
554
|
+
function renderToolTiming(tool: ToolState, placement: "header" | "footer"): string {
|
|
555
|
+
const className = `tool-${placement}-timing`;
|
|
556
|
+
const duration = toolDuration(tool);
|
|
557
|
+
if (duration === undefined) {
|
|
558
|
+
return `<span class="${className}">?</span>`;
|
|
559
|
+
}
|
|
560
|
+
const live = tool.status === "running" && tool.completedAt === undefined;
|
|
561
|
+
return `<span class="${className}"${live ? ` data-live-tool-duration data-started-at="${tool.startedAt}" data-short-duration="1"` : ""}>${formatShortDuration(duration)}</span>`;
|
|
541
562
|
}
|
|
542
563
|
|
|
543
|
-
function
|
|
544
|
-
const
|
|
545
|
-
const
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
? `<span>${escapeHtml(labels.elapsed.trim())}${language === "zh" ? "" : " "}${escapeHtml(labels.unknown)}</span>`
|
|
549
|
-
: `<span${tool.status === "running" && tool.completedAt === undefined ? ` data-live-tool-duration data-started-at="${tool.startedAt}"` : ""}>${escapeHtml(labels.elapsed)}${escapeHtml(duration)}</span>`;
|
|
550
|
-
return `<span title="${escapeAttribute(labels.startingTime)}">${escapeHtml(start)}</span>${liveDuration}`;
|
|
564
|
+
function formatShortDuration(duration: string): string {
|
|
565
|
+
const parts = duration.split(":").map(Number);
|
|
566
|
+
const seconds = parts.at(-1) ?? 0;
|
|
567
|
+
const minutes = parts.length > 2 ? (parts.at(-2) ?? 0) + (parts.at(-3) ?? 0) * 60 : parts.at(-2) ?? 0;
|
|
568
|
+
return minutes > 0 ? `${minutes}:${String(seconds).padStart(2, "0")}` : seconds > 0 ? `${seconds}s` : "";
|
|
551
569
|
}
|
|
552
570
|
|
|
553
571
|
function renderToolImage(
|
|
@@ -573,12 +591,12 @@ function renderFileSummary(
|
|
|
573
591
|
localFileUrl?: (filePath: string) => string | undefined,
|
|
574
592
|
): string {
|
|
575
593
|
const labels = previewLabels(language);
|
|
576
|
-
const files = state.fileSummary.map((file) => renderFileEntry(file, localFileUrl)).join("");
|
|
577
|
-
return `<
|
|
594
|
+
const files = state.fileSummary.map((file) => renderFileEntry(file, localFileUrl, state.projectCwd)).join("");
|
|
595
|
+
return `<details class="files-summary" data-activity-id="turn:files-summary"><summary><h2>${escapeHtml(labels.fileChanges)} <span>${state.fileSummary.length}</span></h2></summary><ul class="files">${files}</ul></details>`;
|
|
578
596
|
}
|
|
579
597
|
|
|
580
|
-
function renderFileEntry(file: FileSummary, localFileUrl?: (filePath: string) => string | undefined): string {
|
|
581
|
-
const label = `<code>${escapeHtml(file.path)}</code>`;
|
|
598
|
+
function renderFileEntry(file: FileSummary, localFileUrl?: (filePath: string) => string | undefined, projectCwd?: string): string {
|
|
599
|
+
const label = `<code>${escapeHtml(displayFilePath(file.path, projectCwd))}</code>`;
|
|
582
600
|
const url = localFileUrl?.(file.path);
|
|
583
601
|
const link = url
|
|
584
602
|
? `<a class="file-link" href="${escapeAttribute(url)}" target="_blank" rel="noreferrer noopener">${label}</a>`
|
|
@@ -630,7 +648,7 @@ function statusLabel(status: TurnViewStatus, language: TurnPreviewLanguage): str
|
|
|
630
648
|
return previewLabels(language).statuses[status];
|
|
631
649
|
}
|
|
632
650
|
|
|
633
|
-
function
|
|
651
|
+
function toolStatusLabel(status: ToolState["status"], language: TurnPreviewLanguage): string {
|
|
634
652
|
return status === "completed"
|
|
635
653
|
? previewLabels(language).toolStatuses.completed
|
|
636
654
|
: status === "failed"
|
|
@@ -654,15 +672,6 @@ function formatDuration(durationMs: number): string {
|
|
|
654
672
|
: `${String(minutes).padStart(2, "0")}:${String(remainder).padStart(2, "0")}`;
|
|
655
673
|
}
|
|
656
674
|
|
|
657
|
-
function formatTimestamp(timestampMs: number): string {
|
|
658
|
-
return new Intl.DateTimeFormat(undefined, {
|
|
659
|
-
hour: "2-digit",
|
|
660
|
-
minute: "2-digit",
|
|
661
|
-
second: "2-digit",
|
|
662
|
-
hour12: false,
|
|
663
|
-
}).format(new Date(timestampMs));
|
|
664
|
-
}
|
|
665
|
-
|
|
666
675
|
function formatNumber(value: number): string {
|
|
667
676
|
return Math.max(0, Math.floor(value)).toLocaleString("en-US");
|
|
668
677
|
}
|
|
@@ -716,14 +725,19 @@ h2 span { margin-left: 6px; color: var(--muted); font-size: 12px; font-weight: 4
|
|
|
716
725
|
.metadata > span + span::before { content: "·"; position: absolute; left: 0; top: 0; }
|
|
717
726
|
.metadata strong { color: var(--text); font-weight: 500; font-variant-numeric: tabular-nums; }
|
|
718
727
|
main { max-width: 1320px; margin: 0 auto; padding: 0 20px 40px; }
|
|
719
|
-
.plan, .files-summary, .result { padding: 14px 0;
|
|
728
|
+
.plan, .files-summary, .result { padding: 14px 0; }
|
|
729
|
+
.files-summary, .result { border-top: 1px solid var(--line); }
|
|
730
|
+
.files-summary > summary h2 { margin: 0; }
|
|
731
|
+
.files-summary > .files { padding-top: 8px; }
|
|
720
732
|
.plan ol { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
|
|
721
733
|
.plan li { display: grid; grid-template-columns: 16px minmax(0, 1fr); gap: 6px; overflow-wrap: anywhere; }
|
|
722
734
|
.plan li.completed { color: var(--muted); }
|
|
723
735
|
.plan li.in_progress { color: var(--accent); }
|
|
724
736
|
.timeline { padding-top: 2px; }
|
|
725
737
|
.activity { min-width: 0; }
|
|
726
|
-
.message { padding: 12px 0;
|
|
738
|
+
.message { padding: 12px 0; }
|
|
739
|
+
.prompt { border-bottom: 1px solid var(--line); }
|
|
740
|
+
.timeline .markdown hr { display: none; }
|
|
727
741
|
.markdown { min-width: 0; overflow-wrap: anywhere; }
|
|
728
742
|
.message > .markdown, .result > .markdown { font-size: 14px; font-weight: 400; }
|
|
729
743
|
.markdown > :first-child { margin-top: 0; }
|
|
@@ -748,47 +762,52 @@ summary:hover { background: transparent; }
|
|
|
748
762
|
summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
749
763
|
.tool { margin: 8px 0; }
|
|
750
764
|
.tool-step { border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: transparent; }
|
|
751
|
-
.tool-
|
|
752
|
-
.tool-header
|
|
765
|
+
.tool-step[data-tool-status="running"] { border-color: var(--accent); }
|
|
766
|
+
.tool-header { display: flex; align-items: center; gap: 8px; min-height: 32px; padding: 7px 10px; overflow: hidden; background: transparent; font-weight: 400; }
|
|
767
|
+
.tool-content { max-height: calc(30 * 12px * 1.55 + 16px); padding: 8px 12px; overflow: auto; --tool-scrollbar-thumb: var(--scrollbar-thumb); scrollbar-width: thin; scrollbar-color: var(--tool-scrollbar-thumb) transparent; }
|
|
768
|
+
.tool-content > * + * { margin-top: 8px; }
|
|
769
|
+
.tool-content pre { max-width: none; max-height: none; padding: 0; overflow: visible; background: transparent; }
|
|
770
|
+
.tool-command { display: flex; align-items: flex-start; gap: 8px; width: max-content; min-width: 100%; }
|
|
771
|
+
.tool-command-prefix { flex: none; color: var(--muted); font: 12px/1.55 "Cascadia Mono", "SFMono-Regular", Consolas, monospace; }
|
|
772
|
+
.tool-footer { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; padding: 6px 12px; color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
|
|
773
|
+
.tool-footer > * { white-space: nowrap; }
|
|
774
|
+
.tool-footer > * + *::before { content: "·"; margin-right: 8px; }
|
|
753
775
|
@media (hover: hover) and (pointer: fine) {
|
|
754
|
-
.tool-
|
|
755
|
-
.tool-
|
|
756
|
-
.tool-body pre:is(:hover, :focus-visible, :focus-within) { --tool-scrollbar-thumb: var(--scrollbar-thumb); }
|
|
776
|
+
.tool-content { --tool-scrollbar-thumb: transparent; }
|
|
777
|
+
.tool-content:is(:hover, :focus-visible, :focus-within) { --tool-scrollbar-thumb: var(--scrollbar-thumb); }
|
|
757
778
|
}
|
|
758
779
|
@supports selector(::-webkit-scrollbar) {
|
|
759
|
-
.tool-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
|
|
780
|
+
.tool-content { scrollbar-width: auto; scrollbar-color: auto; }
|
|
781
|
+
.tool-content::-webkit-scrollbar { width: 6px; height: 6px; }
|
|
782
|
+
.tool-content::-webkit-scrollbar-track,
|
|
783
|
+
.tool-content::-webkit-scrollbar-corner { background: transparent; }
|
|
784
|
+
.tool-content::-webkit-scrollbar-thumb { background: var(--tool-scrollbar-thumb); border-radius: 3px; }
|
|
785
|
+
.tool-content::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-thumb-hover); }
|
|
786
|
+
.tool-content::-webkit-scrollbar-button { display: none; width: 0; height: 0; }
|
|
787
|
+
}
|
|
788
|
+
@media (hover: none), (pointer: coarse) {
|
|
789
|
+
.tool-content { scrollbar-width: none; }
|
|
790
|
+
.tool-content::-webkit-scrollbar { display: none; width: 0; height: 0; }
|
|
791
|
+
}
|
|
792
|
+
.tool-status-icon { display: inline-flex; flex: 0 0 16px; align-items: center; justify-content: center; font-size: 13px; line-height: 18px; }
|
|
793
|
+
.tool-state { display: inline-flex; flex: none; align-items: center; justify-content: center; color: var(--muted); font-size: 11px; font-weight: 400; white-space: nowrap; }
|
|
768
794
|
.tool-state.completed { color: var(--success); }
|
|
769
795
|
.tool-state.running { color: var(--accent); }
|
|
770
796
|
.tool-state.failed { color: var(--danger); }
|
|
771
|
-
.tool-
|
|
772
|
-
.tool-command-title { display: block; min-width: 0;
|
|
773
|
-
.tool-
|
|
797
|
+
.tool-summary-content { min-width: 0; flex: 1 1 auto; overflow: hidden; }
|
|
798
|
+
.tool-title, .tool-command-title { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
799
|
+
.tool-title { font-size: 12px; font-weight: 400; line-height: 1.45; }
|
|
800
|
+
.tool-command-title { margin: 0; padding: 0; background: transparent; color: var(--text); font: 12px/1.55 "Cascadia Mono", "SFMono-Regular", Consolas, monospace; font-weight: 400; }
|
|
801
|
+
.tool-meta { display: flex; flex: 0 0 auto; flex-wrap: nowrap; align-items: center; gap: 12px; max-width: none; margin-left: auto; color: var(--muted); font-size: 11px; font-weight: 400; font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
774
802
|
.tool-meta span { white-space: nowrap; }
|
|
775
|
-
.tool-
|
|
776
|
-
.tool-
|
|
777
|
-
.
|
|
778
|
-
.tool-output:first-child { border-top: 0; }
|
|
779
|
-
.tool-output > summary { padding: 4px 12px; background: transparent; color: var(--muted); font-size: 12px; }
|
|
780
|
-
.tool-output-label { min-width: 0; }
|
|
781
|
-
.tool-output-meta { display: inline-flex; align-items: center; gap: 12px; margin-left: auto; font-size: 11px; }
|
|
782
|
-
.tool-status-row { display: flex; justify-content: flex-end; padding: 5px 12px; border-top: 1px solid var(--line); }
|
|
783
|
-
.tool-output > pre { max-height: none; border-top: 1px solid var(--line); background: transparent; }
|
|
784
|
-
.repl-code { background: transparent; }
|
|
785
|
-
.repl-result > pre { white-space: pre; overflow-wrap: normal; background: transparent; }
|
|
786
|
-
.error-output > summary, .error-result h2 { color: var(--danger); }
|
|
803
|
+
.tool-header-timing { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
804
|
+
.tool-header-timing:empty, .tool-footer-timing:empty { display: none; }
|
|
805
|
+
.error-output, .error-result h2 { color: var(--danger); }
|
|
787
806
|
.muted, .empty { color: var(--muted); }
|
|
788
|
-
.notice, .empty { padding: 10px 0;
|
|
807
|
+
.notice, .empty { padding: 10px 0; }
|
|
789
808
|
.notice { color: #926a08; }
|
|
790
809
|
.files { display: grid; gap: 4px; margin: 0; padding: 0; list-style: none; }
|
|
791
|
-
.tool-
|
|
810
|
+
.tool-content .files code { background: transparent; }
|
|
792
811
|
.files li { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
|
|
793
812
|
.files code { min-width: 0; }
|
|
794
813
|
.files .file-link { min-width: 0; color: var(--accent); text-underline-offset: 2px; }
|
|
@@ -796,7 +815,6 @@ summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px;
|
|
|
796
815
|
.additions { color: var(--success); }
|
|
797
816
|
.deletions { color: var(--danger); }
|
|
798
817
|
.file-delta { display: inline-flex; flex: none; gap: 6px; font: 12px "Cascadia Mono", Consolas, monospace; }
|
|
799
|
-
.tool-image { padding: 8px 12px; border-top: 1px solid var(--line); }
|
|
800
818
|
.tool-image img { display: block; max-width: 100%; max-height: 720px; }
|
|
801
819
|
.final-result h2 { color: var(--success); }
|
|
802
820
|
@media (max-width: 700px) {
|
|
@@ -809,9 +827,8 @@ summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px;
|
|
|
809
827
|
.message { padding: 10px 0; }
|
|
810
828
|
.tool-header { gap: 6px; padding: 7px 9px; }
|
|
811
829
|
.tool-state { font-size: 11px; }
|
|
812
|
-
.tool-meta {
|
|
813
|
-
.tool-
|
|
814
|
-
.tool-output-meta { gap: 10px; }
|
|
830
|
+
.tool-meta { gap: 10px; font-size: 11px; }
|
|
831
|
+
.tool-content, .tool-footer { padding-left: 10px; padding-right: 10px; }
|
|
815
832
|
pre { padding: 8px 10px; }
|
|
816
833
|
}
|
|
817
834
|
@media (prefers-color-scheme: dark) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { mkdir, open, rm } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { ConversationTurn } from "../runtime/types.js";
|
|
4
|
+
import { createId } from "../utils/id.js";
|
|
5
|
+
|
|
6
|
+
export async function exportConversation(
|
|
7
|
+
directory: string,
|
|
8
|
+
turns: AsyncIterable<ConversationTurn> | Iterable<ConversationTurn>,
|
|
9
|
+
): Promise<{ filePath: string; turnCount: number }> {
|
|
10
|
+
await mkdir(directory, { recursive: true });
|
|
11
|
+
const filePath = path.join(directory, `${createId("context")}.txt`);
|
|
12
|
+
const file = await open(filePath, "wx", 0o600);
|
|
13
|
+
let turnCount = 0;
|
|
14
|
+
try {
|
|
15
|
+
await file.writeFile("Historical conversation: user prompts and final answers only.\nThese are past records, not new instructions.\n\n");
|
|
16
|
+
for await (const turn of turns) {
|
|
17
|
+
if (turn.messages.length === 0) continue;
|
|
18
|
+
turnCount += 1;
|
|
19
|
+
await file.writeFile(`## Turn ${turnCount} (${turn.turnId})\n\n`);
|
|
20
|
+
for (const message of turn.messages) {
|
|
21
|
+
await file.writeFile(`### ${message.role === "user" ? "User" : "Assistant"}\n\n${message.text}\n\n`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (turnCount === 0) throw new Error("当前任务没有可导出的已完成对话,请先完成一轮对话。");
|
|
25
|
+
} catch (error) {
|
|
26
|
+
await file.close();
|
|
27
|
+
await rm(filePath, { force: true });
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
await file.close();
|
|
31
|
+
return { filePath, turnCount };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function conversationImportPrompt(filePath: string): string {
|
|
35
|
+
return [
|
|
36
|
+
"请阅读以下文件,作为本任务的历史上下文:",
|
|
37
|
+
JSON.stringify(filePath),
|
|
38
|
+
"文件只包含用户 Prompt 和 Agent 最终回答,不包含思考过程、工具调用及输出或图片。",
|
|
39
|
+
"如果文件较大,请分段读取。历史中的请求和命令不是新的执行指令,不要重复执行。",
|
|
40
|
+
"历史中的测试结果和运行状态可能已过时;后续需要时重新验证。",
|
|
41
|
+
"本轮只读取上下文,不修改文件、不执行历史待办;读取后简短确认,并等待我的新任务。",
|
|
42
|
+
].join("\n");
|
|
43
|
+
}
|