@my-life-buddies/cli 0.13.3 → 0.15.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/README.md +74 -22
- package/dist/bin/buddy.js +29 -10
- package/dist/bin/buddy.js.map +2 -2
- package/dist/bin/core.js +1480 -611
- package/dist/bin/core.js.map +4 -4
- package/dist/bin/preview.js +324 -27
- package/dist/bin/preview.js.map +4 -4
- package/dist/web/app.css +65 -6
- package/dist/web/app.js +103 -39
- package/dist/web/diagnostics.js +184 -0
- package/dist/web/index.html +34 -34
- package/dist/web/widgets.css +59 -28
- package/dist/web/widgets.js +230 -76
- package/package.json +3 -3
- package/resources/README.md +6 -50
- package/resources/agent-template/package-lock.json +4 -4
- package/resources/agent-template/package.json +2 -2
- package/resources/buddy-creator/INSTALL.md +0 -25
- package/resources/buddy-creator/PATCHES.md +0 -53
- package/resources/buddy-creator/SKILL.md +0 -76
- package/resources/buddy-creator/THIRD_PARTY_NOTICES.md +0 -154
- package/resources/buddy-creator/agents/openai.yaml +0 -4
- package/resources/buddy-creator/assets/licenses/bail.txt +0 -26
- package/resources/buddy-creator/assets/licenses/ccount.txt +0 -26
- package/resources/buddy-creator/assets/licenses/character-entities-html4.txt +0 -26
- package/resources/buddy-creator/assets/licenses/character-entities-legacy.txt +0 -26
- package/resources/buddy-creator/assets/licenses/character-entities.txt +0 -26
- package/resources/buddy-creator/assets/licenses/character-reference-invalid.txt +0 -26
- package/resources/buddy-creator/assets/licenses/comma-separated-tokens.txt +0 -26
- package/resources/buddy-creator/assets/licenses/debug.txt +0 -24
- package/resources/buddy-creator/assets/licenses/decode-named-character-reference.txt +0 -26
- package/resources/buddy-creator/assets/licenses/dequal.txt +0 -25
- package/resources/buddy-creator/assets/licenses/devlop.txt +0 -26
- package/resources/buddy-creator/assets/licenses/escape-string-regexp.txt +0 -13
- package/resources/buddy-creator/assets/licenses/estree-util-is-identifier-name.txt +0 -26
- package/resources/buddy-creator/assets/licenses/extend.txt +0 -27
- package/resources/buddy-creator/assets/licenses/hast-util-to-jsx-runtime.txt +0 -26
- package/resources/buddy-creator/assets/licenses/hast-util-whitespace.txt +0 -26
- package/resources/buddy-creator/assets/licenses/html-url-attributes.txt +0 -25
- package/resources/buddy-creator/assets/licenses/inline-style-parser.txt +0 -13
- package/resources/buddy-creator/assets/licenses/is-alphabetical.txt +0 -26
- package/resources/buddy-creator/assets/licenses/is-alphanumerical.txt +0 -26
- package/resources/buddy-creator/assets/licenses/is-decimal.txt +0 -26
- package/resources/buddy-creator/assets/licenses/is-hexadecimal.txt +0 -26
- package/resources/buddy-creator/assets/licenses/is-plain-obj.txt +0 -13
- package/resources/buddy-creator/assets/licenses/longest-streak.txt +0 -26
- package/resources/buddy-creator/assets/licenses/markdown-table.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-find-and-replace.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-from-markdown.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-gfm-autolink-literal.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-gfm-footnote.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-gfm-strikethrough.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-gfm-table.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-gfm-task-list-item.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-gfm.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-mdx-expression.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-mdx-jsx.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-mdxjs-esm.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-phrasing.txt +0 -27
- package/resources/buddy-creator/assets/licenses/mdast-util-to-hast.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-to-markdown.txt +0 -26
- package/resources/buddy-creator/assets/licenses/mdast-util-to-string.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-core-commonmark.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-autolink-literal.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-footnote.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-strikethrough.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-table.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-tagfilter.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-extension-gfm-task-list-item.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-extension-gfm.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-factory-destination.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-factory-label.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-factory-space.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-factory-title.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-factory-whitespace.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-character.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-chunked.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-classify-character.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-combine-extensions.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-decode-numeric-character-reference.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-decode-string.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-encode.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-html-tag-name.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-normalize-identifier.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-resolve-all.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-sanitize-uri.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-subtokenize.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-symbol.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark-util-types.txt +0 -26
- package/resources/buddy-creator/assets/licenses/micromark.txt +0 -26
- package/resources/buddy-creator/assets/licenses/ms.txt +0 -25
- package/resources/buddy-creator/assets/licenses/parse-entities.txt +0 -26
- package/resources/buddy-creator/assets/licenses/phosphor-icons--react.txt +0 -25
- package/resources/buddy-creator/assets/licenses/property-information.txt +0 -26
- package/resources/buddy-creator/assets/licenses/react-dom.txt +0 -25
- package/resources/buddy-creator/assets/licenses/react-markdown.txt +0 -25
- package/resources/buddy-creator/assets/licenses/react.txt +0 -25
- package/resources/buddy-creator/assets/licenses/remark-gfm.txt +0 -26
- package/resources/buddy-creator/assets/licenses/remark-parse.txt +0 -25
- package/resources/buddy-creator/assets/licenses/remark-rehype.txt +0 -26
- package/resources/buddy-creator/assets/licenses/remark-stringify.txt +0 -25
- package/resources/buddy-creator/assets/licenses/scheduler.txt +0 -25
- package/resources/buddy-creator/assets/licenses/space-separated-tokens.txt +0 -26
- package/resources/buddy-creator/assets/licenses/stringify-entities.txt +0 -26
- package/resources/buddy-creator/assets/licenses/style-to-js.txt +0 -26
- package/resources/buddy-creator/assets/licenses/style-to-object.txt +0 -26
- package/resources/buddy-creator/assets/licenses/trim-lines.txt +0 -26
- package/resources/buddy-creator/assets/licenses/trough.txt +0 -25
- package/resources/buddy-creator/assets/licenses/types--debug.txt +0 -25
- package/resources/buddy-creator/assets/licenses/types--estree-jsx.txt +0 -25
- package/resources/buddy-creator/assets/licenses/types--estree.txt +0 -25
- package/resources/buddy-creator/assets/licenses/types--hast.txt +0 -25
- package/resources/buddy-creator/assets/licenses/types--mdast.txt +0 -25
- package/resources/buddy-creator/assets/licenses/types--ms.txt +0 -25
- package/resources/buddy-creator/assets/licenses/types--unist.txt +0 -25
- package/resources/buddy-creator/assets/licenses/ungap--structured-clone.txt +0 -19
- package/resources/buddy-creator/assets/licenses/unified.txt +0 -25
- package/resources/buddy-creator/assets/licenses/unist-util-is.txt +0 -26
- package/resources/buddy-creator/assets/licenses/unist-util-position.txt +0 -26
- package/resources/buddy-creator/assets/licenses/unist-util-stringify-position.txt +0 -26
- package/resources/buddy-creator/assets/licenses/unist-util-visit-parents.txt +0 -26
- package/resources/buddy-creator/assets/licenses/unist-util-visit.txt +0 -26
- package/resources/buddy-creator/assets/licenses/vfile-message.txt +0 -26
- package/resources/buddy-creator/assets/licenses/vfile.txt +0 -25
- package/resources/buddy-creator/assets/licenses/zwitch.txt +0 -26
- package/resources/buddy-creator/assets/preview/assets/index-B5VmkoSx.js +0 -39
- package/resources/buddy-creator/assets/preview/assets/index-DWKP6b-3.css +0 -4
- package/resources/buddy-creator/assets/preview/brand-logo.png +0 -0
- package/resources/buddy-creator/assets/preview/brand.css +0 -100
- package/resources/buddy-creator/assets/preview/index.html +0 -7
- package/resources/buddy-creator/assets/preview/reader.css +0 -165
- package/resources/buddy-creator/assets/preview/reader.js +0 -135
- package/resources/buddy-creator/assets/preview/widgets.css +0 -1
- package/resources/buddy-creator/assets/preview/widgets.js +0 -168
- package/resources/buddy-creator/assets/widget-reference/conversation.png +0 -0
- package/resources/buddy-creator/assets/widget-reference/expanded.png +0 -0
- package/resources/buddy-creator/assets/widget-v2/app.js +0 -62
- package/resources/buddy-creator/assets/widget-v2/style.css +0 -1
- package/resources/buddy-creator/assets/widget-v3/app.js +0 -65
- package/resources/buddy-creator/assets/widget-v3/style.css +0 -103
- package/resources/buddy-creator/assets/widget-v4/app.js +0 -66
- package/resources/buddy-creator/assets/widget-v4/style.css +0 -187
- package/resources/buddy-creator/references/artifact-schema.md +0 -70
- package/resources/buddy-creator/references/catalog.json +0 -401
- package/resources/buddy-creator/references/definition.md +0 -24
- package/resources/buddy-creator/references/dialogue-examples.md +0 -133
- package/resources/buddy-creator/references/host-guide.md +0 -219
- package/resources/buddy-creator/references/interview.md +0 -235
- package/resources/buddy-creator/references/knowledge.md +0 -40
- package/resources/buddy-creator/references/methods.md +0 -77
- package/resources/buddy-creator/references/opening.md +0 -14
- package/resources/buddy-creator/references/preview-panel.md +0 -66
- package/resources/buddy-creator/references/recovery.md +0 -46
- package/resources/buddy-creator/references/service.md +0 -76
- package/resources/buddy-creator/references/widgets-protocol.md +0 -83
- package/resources/buddy-creator/references/widgets.md +0 -99
- package/resources/buddy-creator/scripts/buddy.py +0 -108
- package/resources/buddy-creator/scripts/buddy_client.py +0 -67
- package/resources/buddy-creator/scripts/buddy_core.py +0 -865
- package/resources/buddy-creator/scripts/completion.py +0 -296
- package/resources/buddy-creator/scripts/interview.py +0 -215
- package/resources/buddy-creator/scripts/preview.py +0 -617
- package/resources/buddy-creator/scripts/preview_panel.py +0 -80
- package/resources/buddy-creator/scripts/sources.py +0 -239
- package/resources/buddy-creator/scripts/widget_render.py +0 -66
- package/resources/buddy-creator/scripts/widget_render_v1.py +0 -176
- package/resources/buddy-creator/scripts/widget_render_v2.py +0 -98
- package/resources/buddy-creator/scripts/widget_render_v3.py +0 -86
- package/resources/buddy-creator/scripts/widget_render_v4.py +0 -121
- package/resources/buddy-creator/scripts/widgets.py +0 -227
- package/resources/buddy-creator/version.json +0 -8
- package/resources/buddy-creator.manifest.json +0 -791
- package/resources/dada-object-avatar/SKILL.md +0 -37
- package/resources/dada-object-avatar/agents/openai.yaml +0 -4
- package/resources/dada-object-avatar/assets/examples/cat.png +0 -0
- package/resources/dada-object-avatar/assets/examples/cooking.png +0 -0
- package/resources/dada-object-avatar/assets/examples/divination.png +0 -0
- package/resources/dada-object-avatar/assets/examples/doctor.png +0 -0
- package/resources/dada-object-avatar/assets/examples/relationship.png +0 -0
- package/resources/dada-object-avatar/assets/examples/work.png +0 -0
- package/resources/dada-object-avatar/assets/preview.png +0 -0
- package/resources/dada-object-avatar/references/evaluation-rubric.md +0 -22
- package/resources/dada-object-avatar/references/examples.json +0 -8
- package/resources/dada-object-avatar/references/prompt-template.md +0 -27
- package/resources/dada-object-avatar/references/style-system.md +0 -54
- package/resources/dada-object-avatar/references/subject-design.md +0 -23
- package/resources/dada-object-avatar/references/training-guide.md +0 -14
- package/resources/dada-object-avatar/references/training-log.jsonl +0 -0
- package/resources/dada-object-avatar/references/training-record.schema.json +0 -20
- package/resources/dada-object-avatar/scripts/choose_gaze.py +0 -36
- package/resources/dada-object-avatar/scripts/record_example.py +0 -22
- package/resources/dada-object-avatar/scripts/validate_png.py +0 -52
- package/resources/dada-object-avatar.manifest.json +0 -85
package/dist/web/app.css
CHANGED
|
@@ -326,7 +326,8 @@
|
|
|
326
326
|
.empty-state p { max-width: 30rem; margin: 0; color: var(--color-ink-soft); font-size: var(--text-sm); line-height: 1.65; }
|
|
327
327
|
|
|
328
328
|
.waterfall { display: grid; gap: var(--space-md); margin-block-start: var(--space-xl); padding-block-end: var(--space-lg); border-block-end: var(--rule-hair) solid var(--color-rule); }
|
|
329
|
-
.
|
|
329
|
+
.timing-heading { display:flex; justify-content:space-between; flex-wrap:wrap; gap:6px 16px; color:var(--color-muted); font-size:11px; line-height:1.7; }
|
|
330
|
+
.waterfall-row { display: grid; grid-template-columns: 6rem minmax(2rem, 1fr) 8.5rem; align-items: center; gap: var(--space-md); font-size: var(--text-xs); }
|
|
330
331
|
.waterfall-track { height: 0.375rem; overflow: hidden; border-radius: var(--radius-pill); background: var(--color-paper-soft); }
|
|
331
332
|
.waterfall-fill { display: block; height: 100%; min-width: var(--space-2xs); transform-origin: left; border-radius: inherit; background: var(--color-ink); }
|
|
332
333
|
.waterfall-row strong { font-family: var(--font-mono); font-size: 0.6875rem; font-weight: 500; text-align: end; }
|
|
@@ -553,20 +554,21 @@ body[data-hosted-preview] .hosted-page-heading p { display:flex; align-items:cen
|
|
|
553
554
|
body[data-hosted-preview] .hosted-page-heading p span:first-child { overflow:hidden; white-space:nowrap; text-overflow:ellipsis; max-width:60%; }
|
|
554
555
|
body[data-hosted-preview] .hosted-page-heading h1 { margin:0; font-size:26px; line-height:1.3; letter-spacing:-.035em; font-weight:650; }
|
|
555
556
|
body[data-hosted-preview] .local-connection { border-top:1px solid var(--color-rule); }
|
|
556
|
-
|
|
557
|
+
/* Hosted tabs are normal flex content, not the standalone sticky sidebar. */
|
|
558
|
+
body[data-hosted-preview] .debugger > .sidebar { flex:none; position:static; z-index:auto; width:auto; padding:0 20px; border:0; border-bottom:1px solid var(--color-rule); }
|
|
557
559
|
body[data-hosted-preview] .sidebar-section { margin:0; }
|
|
558
560
|
body[data-hosted-preview] .debug-tabs { display:flex; flex-wrap:nowrap; gap:20px; margin:0; }
|
|
559
561
|
body[data-hosted-preview] .debug-tabs button { width:auto; min-height:44px; padding:10px 0; font-size:13px; gap:7px; border-radius:0; white-space:nowrap; }
|
|
560
562
|
body[data-hosted-preview] .debug-tabs button svg { width:15px; height:15px; }
|
|
561
563
|
body[data-hosted-preview] .debug-tabs button[aria-selected="true"] { background:transparent; box-shadow:inset 0 -2px var(--color-accent); }
|
|
562
564
|
body[data-hosted-preview] .run-strip { flex:none; padding:18px 20px 14px; border-bottom:1px solid var(--color-rule); }
|
|
563
|
-
body[data-hosted-preview] .run-heading { align-items:center; flex-wrap:
|
|
565
|
+
body[data-hosted-preview] .run-heading { align-items:center; flex-wrap:wrap; gap:16px; }
|
|
564
566
|
body[data-hosted-preview] .run-identity { flex:1; }
|
|
565
567
|
body[data-hosted-preview] .run-identity > .eyebrow { display:none; }
|
|
566
568
|
body[data-hosted-preview] .run-title-line { margin:0; flex-wrap:nowrap; gap:8px; }
|
|
567
569
|
body[data-hosted-preview] .run-title-line h2 { font-size:17px; letter-spacing:0; }
|
|
568
570
|
body[data-hosted-preview] .status-pill { font-size:11px; padding:3px 7px; }
|
|
569
|
-
body[data-hosted-preview] .run-controls { flex:0 1
|
|
571
|
+
body[data-hosted-preview] .run-controls { flex:0 1 340px; min-width:min(100%,340px); max-width:100%; gap:6px; }
|
|
570
572
|
body[data-hosted-preview] .run-picker-label { position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%); }
|
|
571
573
|
body[data-hosted-preview] :is(.run-picker,.run-step) { min-height:30px; border-radius:6px; font-size:11px; padding-block:4px; }
|
|
572
574
|
body[data-hosted-preview] .run-step { width:30px; }
|
|
@@ -594,7 +596,7 @@ body[data-hosted-preview] .run-summary-copy { margin:16px 0 0; font-size:13px; l
|
|
|
594
596
|
.run-progress li[data-stage="active"] { border-color:var(--color-accent); color:var(--color-accent); font-weight:600; }
|
|
595
597
|
.run-progress li[data-stage="error"] { border-color:var(--color-error); color:var(--color-error); }
|
|
596
598
|
body[data-hosted-preview] .waterfall { margin:0; padding:0; border:0; gap:12px; }
|
|
597
|
-
body[data-hosted-preview] .waterfall-row { grid-template-columns:
|
|
599
|
+
body[data-hosted-preview] .waterfall-row { grid-template-columns:84px minmax(0,1fr) 132px; gap:12px; font-size:12px; }
|
|
598
600
|
body[data-hosted-preview] .waterfall-track { height:3px; background:transparent; }
|
|
599
601
|
body[data-hosted-preview] .waterfall-fill { min-width:0; background:var(--color-accent); opacity:.45; }
|
|
600
602
|
body[data-hosted-preview] .workbench-state { flex:none; padding:26px 24px; border-bottom:1px solid var(--color-rule); }
|
|
@@ -602,7 +604,7 @@ body[data-hosted-preview] .workbench-state h2 { font-size:18px; font-weight:600;
|
|
|
602
604
|
body[data-hosted-preview] .workbench-state p { max-width:480px; color:var(--color-muted); font-size:13px; line-height:1.8; margin:0; }
|
|
603
605
|
body[data-hosted-preview] .state-mark { display:block; width:32px; height:3px; margin-bottom:20px; background:var(--color-accent); border-radius:2px; }
|
|
604
606
|
body[data-hosted-preview][data-has-runs="false"] .run-strip { display:none; }
|
|
605
|
-
|
|
607
|
+
|
|
606
608
|
body[data-hosted-preview][data-dev-phase="disconnected"][data-has-runs="false"] .workbench-state { padding-top:56px; padding-bottom:56px; }
|
|
607
609
|
body[data-hosted-preview][data-dev-phase="disconnected"] .topbar-actions { display:none; }
|
|
608
610
|
body[data-hosted-preview][data-dev-phase="disconnected"] :is(.run-progress li,.replying-indicator i) { animation-play-state:paused; }
|
|
@@ -660,3 +662,60 @@ body[data-hosted-preview] .pairing-panel { top:80px; }
|
|
|
660
662
|
body[data-hosted-preview] .run-controls { flex-basis:100%; }
|
|
661
663
|
body[data-hosted-preview] .simulator-area { padding-inline:12px; }
|
|
662
664
|
}
|
|
665
|
+
|
|
666
|
+
/* Runtime diagnostics share the existing Preview typography and semantic colors. */
|
|
667
|
+
.diagnostic-source { flex:none; color:var(--color-muted); font-size:11px; }
|
|
668
|
+
.diagnostic-filters { display:flex; flex-wrap:wrap; gap:12px; margin:20px 0 12px; }
|
|
669
|
+
.diagnostic-filters label { display:flex; flex:1 1 130px; min-width:0; flex-direction:column; gap:6px; color:var(--color-ink-soft); font-size:11px; }
|
|
670
|
+
.diagnostic-filters .diagnostic-search { flex:1.5 1 160px; }
|
|
671
|
+
.diagnostic-filters :is(select,input) { width:100%; min-width:0; min-height:36px; padding:7px 10px; border:1px solid var(--color-rule-strong); border-radius:6px; background:var(--color-paper-raised); color:var(--color-ink); font:inherit; font-size:12px; }
|
|
672
|
+
.diagnostic-filters :is(select,input):focus-visible { outline:2px solid var(--color-accent); outline-offset:2px; }
|
|
673
|
+
.diagnostic-note { color:var(--color-muted); font-size:11px; line-height:1.8; margin:12px 0 16px; }
|
|
674
|
+
.diagnostic-entry { border-bottom:1px solid var(--color-rule); min-width:0; }
|
|
675
|
+
.diagnostic-entry > summary { display:flex; align-items:start; gap:10px; padding:16px 0; cursor:pointer; list-style:none; }
|
|
676
|
+
.diagnostic-entry > summary::-webkit-details-marker { display:none; }
|
|
677
|
+
.diagnostic-entry > summary::after { content:'›'; color:var(--color-muted); font-size:18px; line-height:20px; flex:none; }
|
|
678
|
+
.diagnostic-entry[open] > summary::after { transform:rotate(90deg); }
|
|
679
|
+
.diagnostic-entry > summary:hover .diagnostic-heading strong { color:var(--color-accent); }
|
|
680
|
+
.diagnostic-entry > summary:focus-visible { outline:2px solid var(--color-accent); outline-offset:3px; border-radius:3px; }
|
|
681
|
+
.diagnostic-heading { display:flex; flex-direction:column; gap:5px; min-width:0; flex:1; }
|
|
682
|
+
.diagnostic-heading strong { font-size:13px; font-weight:550; line-height:1.5; overflow-wrap:anywhere; }
|
|
683
|
+
.diagnostic-preview { color:var(--color-muted); font-size:12px; line-height:1.6; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
684
|
+
.diagnostic-badge { flex:none; color:var(--color-ink-soft); background:var(--color-paper-soft); border-radius:4px; padding:3px 5px; min-width:36px; font:10px/1.4 var(--font-mono); text-align:center; }
|
|
685
|
+
.diagnostic-badge[data-kind="response"] { color:var(--color-accent); background:var(--color-accent-soft, var(--color-paper-soft)); }
|
|
686
|
+
.diagnostic-entry[data-level="error"] > summary .diagnostic-badge { color:var(--color-error); background:var(--color-error-soft); }
|
|
687
|
+
.diagnostic-entry[data-level="warn"] > summary .diagnostic-badge { color:var(--color-warning); background:var(--color-warning-soft); }
|
|
688
|
+
.diagnostic-entry time { color:var(--color-muted); font:11px/1.8 var(--font-mono); white-space:nowrap; }
|
|
689
|
+
.diagnostic-body { padding:0 0 18px; }
|
|
690
|
+
.diagnostic-meta { display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:4px 12px; color:var(--color-muted); font-size:11px; margin:0 0 12px; }
|
|
691
|
+
.diagnostic-meta > span { overflow-wrap:anywhere; min-width:0; }
|
|
692
|
+
.diagnostic-meta button { font-size:11px; }
|
|
693
|
+
.diagnostic-section { border:1px solid var(--color-rule); border-radius:6px; margin:8px 0 0; overflow:hidden; }
|
|
694
|
+
.diagnostic-section > summary { cursor:pointer; padding:10px 12px; font-size:12px; color:var(--color-ink-soft); }
|
|
695
|
+
.diagnostic-section[open] > summary { border-bottom:1px solid var(--color-rule); }
|
|
696
|
+
.diagnostic-section pre { margin:0; padding:14px; max-height:420px; overflow:auto; white-space:pre-wrap; overflow-wrap:anywhere; font:11px/1.85 var(--font-mono); background:var(--color-paper-soft); tab-size:2; }
|
|
697
|
+
.diagnostic-metrics { display:flex; flex-wrap:wrap; gap:16px 24px; margin:18px 0; }
|
|
698
|
+
.diagnostic-metrics div { min-width:70px; }
|
|
699
|
+
.diagnostic-metrics dt { color:var(--color-muted); font-size:10px; margin-bottom:6px; }
|
|
700
|
+
.diagnostic-metrics dd { margin:0; font:12px var(--font-mono); color:var(--color-ink); }
|
|
701
|
+
.metric-help { max-width:220px; margin:6px 0 0; color:var(--color-muted); font:11px/1.7 var(--font-body); white-space:normal; overflow-wrap:anywhere; }
|
|
702
|
+
.run-metrics dd:has(.metric-help) { overflow:visible; white-space:normal; }
|
|
703
|
+
.diagnostic-warning { color:var(--color-error); background:var(--color-error-soft); padding:10px 12px; font-size:12px; line-height:1.8; border-radius:6px; overflow-wrap:anywhere; }
|
|
704
|
+
.diagnostic-empty { padding:44px 0 52px; }
|
|
705
|
+
.diagnostic-empty strong { font-size:15px; font-weight:550; }
|
|
706
|
+
.diagnostic-empty p { max-width:420px; color:var(--color-muted); font-size:12px; line-height:1.9; }
|
|
707
|
+
.diagnostic-explanation { margin-top:24px; color:var(--color-muted); font-size:11px; line-height:1.9; }
|
|
708
|
+
.diagnostic-explanation summary { cursor:pointer; }
|
|
709
|
+
.diagnostic-explanation p { margin:10px 0; }
|
|
710
|
+
.diagnostic-history { margin-top:24px; padding-top:16px; border-top:1px solid var(--color-rule); }
|
|
711
|
+
.diagnostic-history > summary { cursor:pointer; font-size:13px; color:var(--color-ink-soft); }
|
|
712
|
+
.diagnostic-history #context-source { font-size:10px; margin-left:8px; color:var(--color-muted); }
|
|
713
|
+
#panel-overview .run-strip, body[data-hosted-preview] #panel-overview .run-strip { padding:0 0 16px; }
|
|
714
|
+
#panel-protocol .protocol-log { display:block; border:0; }
|
|
715
|
+
@media (max-width:650px) {
|
|
716
|
+
.diagnostic-filters { gap:10px; }
|
|
717
|
+
.diagnostic-filters .diagnostic-search { flex-basis:100%; }
|
|
718
|
+
.diagnostic-entry > summary { gap:7px; }
|
|
719
|
+
.diagnostic-entry time { font-size:10px; }
|
|
720
|
+
.diagnostic-source { display:none; }
|
|
721
|
+
}
|
package/dist/web/app.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
const connectionUrl = hosted ? new URL(`../api/buddies/${encodeURIComponent(hostedBuddyId)}/preview`, location.href) : undefined;
|
|
7
7
|
let connectionInstance = "", connectionReady = !hosted, connectionBusy = false, devPollBusy = false;
|
|
8
8
|
let connectionChecked = false, connectionFailure = "", devAction = "";
|
|
9
|
+
let eventSource, eventSourceInstance = "", liveEventsActive = false, pollingInterval = 0;
|
|
9
10
|
function previewApiUrl(path) {
|
|
10
11
|
if (!hosted) return path;
|
|
11
12
|
const url = new URL(connectionUrl.href + path);
|
|
@@ -190,6 +191,8 @@
|
|
|
190
191
|
idle: hosted ? "已连接但 DEV 未启动" : "未启动",
|
|
191
192
|
disconnected: "未连接本地工程",
|
|
192
193
|
starting: "正在启动",
|
|
194
|
+
reloading: "正在重载",
|
|
195
|
+
reload_failed: "重载失败 · 等待修正",
|
|
193
196
|
ready: "DEV 运行中",
|
|
194
197
|
stopping: "正在停止",
|
|
195
198
|
failed: "启动失败",
|
|
@@ -200,6 +203,7 @@
|
|
|
200
203
|
if (!next || !Number.isFinite(next.revision)) return false;
|
|
201
204
|
if (Number.isFinite(state.dev?.revision) && next.revision < state.dev.revision) return false;
|
|
202
205
|
if (next.phase === "ready" && state.dev.phase !== "ready") state.simulatorLoaded = false;
|
|
206
|
+
if (state.dev.phase === "reload_failed" && ["ready", "reloading"].includes(next.phase)) elements.errorBanner.hidden = true;
|
|
203
207
|
state.dev = next;
|
|
204
208
|
if (next.ready?.buddy) state.buddy = next.ready.buddy;
|
|
205
209
|
return true;
|
|
@@ -253,8 +257,8 @@
|
|
|
253
257
|
elements.connectionState.dataset.phase = phase;
|
|
254
258
|
elements.connectionLabel.textContent = phase === "disconnected" ? "未连接本地工程" : phaseCopy(phase);
|
|
255
259
|
elements.devControl.disabled = !connectionReady || Boolean(devAction) || phase === "starting" || phase === "stopping";
|
|
256
|
-
elements.devControl.dataset.action =
|
|
257
|
-
elements.devControl.textContent = devAction === "start" ? "正在启动…" : devAction === "stop" ? "正在停止…" : phase === "failed" ? "重试启动" :
|
|
260
|
+
elements.devControl.dataset.action = ["ready", "reloading", "reload_failed"].includes(phase) ? "stop" : "start";
|
|
261
|
+
elements.devControl.textContent = devAction === "start" ? "正在启动…" : devAction === "stop" ? "正在停止…" : phase === "failed" ? "重试启动" : ["ready", "reloading", "reload_failed"].includes(phase) ? "停止 DEV" : phase === "starting" ? "正在启动…" : phase === "stopping" ? "正在停止…" : "启动 DEV";
|
|
258
262
|
elements.environmentAgent.textContent = phase === "ready"
|
|
259
263
|
? `${state.dev.ready?.agentId || "已就绪"} · CLI 托管`
|
|
260
264
|
: phaseCopy(phase);
|
|
@@ -271,12 +275,12 @@
|
|
|
271
275
|
elements.resetConversation.disabled = phase !== "ready" || Boolean(active) || state.resetting || state.sending || state.resetUnavailable;
|
|
272
276
|
elements.resetConversation.title = state.resetUnavailable ? "当前平台暂不支持重开会话" : active ? "请等待当前回复完成后再清空" : "结束当前开发版对话,开始一段全新会话";
|
|
273
277
|
elements.devControl.disabled ||= state.resetting;
|
|
274
|
-
elements.messageInput.disabled = phase !== "ready" || !state.simulatorLoaded ||
|
|
275
|
-
elements.sendMessage.hidden =
|
|
278
|
+
elements.messageInput.disabled = phase !== "ready" || !state.simulatorLoaded || state.resetting || state.sending;
|
|
279
|
+
elements.sendMessage.hidden = false;
|
|
276
280
|
updateSendButton();
|
|
277
281
|
syncPolling();
|
|
278
282
|
if (
|
|
279
|
-
phase === "failed"
|
|
283
|
+
(phase === "failed" || phase === "reload_failed")
|
|
280
284
|
&& state.dev.error?.message
|
|
281
285
|
&& state.reportedFailureRevision !== state.dev.revision
|
|
282
286
|
) {
|
|
@@ -300,17 +304,19 @@
|
|
|
300
304
|
const copy = !connectionChecked ? ["正在检查连接", "读取本地工程的连接状态。"]
|
|
301
305
|
: phase === "disconnected" ? [connectionInstance ? "本地连接已断开" : "连接本地工程", connectionFailure || (connectionInstance ? "正在等待重新连接。保留当前记录,连接恢复后继续调试。" : "点击上方「连接工程」,复制指令到终端运行。")]
|
|
302
306
|
: phase === "starting" ? ["正在启动调试", "正在准备运行环境,启动完成后即可发送消息。"]
|
|
307
|
+
: phase === "reloading" ? ["正在重载", "本地文件已修改,正在重新加载;当前回复可能中断。"]
|
|
308
|
+
: phase === "reload_failed" ? ["重载失败", state.dev.error?.message || "修正文件并保存后自动重试,也可停止 DEV。"]
|
|
303
309
|
: phase === "stopping" ? ["正在停止调试", "等待运行环境退出,当前记录会保留。"]
|
|
304
310
|
: phase === "failed" ? ["调试未能启动", "查看上方错误信息,修复工程后点击「重试启动」。"]
|
|
305
311
|
: phase === "idle" ? ["工程已连接", "点击上方「启动 DEV」,开始体验搭子。"]
|
|
306
312
|
: !state.simulatorLoaded ? ["正在读取对话", "同步当前会话,完成后即可发送消息。"]
|
|
307
|
-
: state.sending ? ["正在发送消息", "
|
|
308
|
-
: active ? ["等待执行记录", "
|
|
309
|
-
: ["开始一轮调试", history ? "
|
|
310
|
-
guide.hidden = phase === "ready" && runs.length > 0;
|
|
313
|
+
: state.sending ? ["正在发送消息", "提交成功后,这里会显示消息与模型日志。"]
|
|
314
|
+
: active ? ["等待执行记录", "消息已提交,正在同步消息与模型日志。"]
|
|
315
|
+
: ["开始一轮调试", history ? "右侧保留了历史对话。本地调试记录从本次连接开始,发一条新消息即可查看。" : "在右侧发一条消息,观察搭子的回复与模型日志。"];
|
|
316
|
+
guide.hidden = phase === "ready" && (runs.length > 0 || (state.debug.diagnostics?.records || []).some(record => record.kind !== "log"));
|
|
311
317
|
$("#workbench-state-title").textContent = copy[0];
|
|
312
318
|
$("#workbench-state-copy").textContent = copy[1];
|
|
313
|
-
$("#connection-description").textContent = !connectionChecked ? "检查中" : phase === "disconnected" ? "等待连接" : "
|
|
319
|
+
$("#connection-description").textContent = !connectionChecked ? "检查中" : phase === "disconnected" ? "等待连接" : "保存自动重载,可能中断回复;Widget 数据不自动迁移。";
|
|
314
320
|
const connectButton = $("#get-connect-command");
|
|
315
321
|
connectButton.className = `button ${connectionReady ? "button-secondary" : "button-primary"}`;
|
|
316
322
|
connectButton.textContent = connectionReady ? "连接指令" : connectionInstance ? "重新连接" : "连接工程";
|
|
@@ -321,7 +327,7 @@
|
|
|
321
327
|
if (!state.project) elements.buddyName.textContent = new URL(location.href).searchParams.get("buddyName") || "我的搭子";
|
|
322
328
|
const empty = elements.chatMessages.querySelector(".chat-empty");
|
|
323
329
|
if (empty) empty.textContent = phase === "ready" ? "发一条消息,开始体验" : phase === "disconnected" ? "连接后,在这里体验搭子的回复" : "启动 DEV 后,开始对话";
|
|
324
|
-
elements.messageInput.placeholder = active ? "
|
|
330
|
+
elements.messageInput.placeholder = active ? "可以继续补充消息" : phase === "ready" ? "给搭子发一条消息" : "等待调试就绪";
|
|
325
331
|
}
|
|
326
332
|
|
|
327
333
|
function runOrdinal(run, index, runs) {
|
|
@@ -332,7 +338,7 @@
|
|
|
332
338
|
|
|
333
339
|
function runOptionLabel(run, index, runs) {
|
|
334
340
|
const date = dayKey(run.startedAt) === dayKey(new Date()) ? "" : `${dayLabel(run.startedAt) || ""} `;
|
|
335
|
-
return `第 ${runOrdinal(run, index, runs)} 轮 · ${date}${formatTime(run.startedAt)} · ${runStatusCopy(run.status)}`;
|
|
341
|
+
return `第 ${runOrdinal(run, index, runs)} 轮 · ${date}${formatTime(run.startedAt)} · ${(run.completion === "no_reply" && run.status === "completed" ? "未观察到回复" : runStatusCopy(run.status))}`;
|
|
336
342
|
}
|
|
337
343
|
|
|
338
344
|
let runPickerSignature = "";
|
|
@@ -372,10 +378,10 @@
|
|
|
372
378
|
elements.runId.title = run?.runId || "";
|
|
373
379
|
elements.copyRunId.disabled = !run;
|
|
374
380
|
elements.runStatus.dataset.status = run?.status || "idle";
|
|
375
|
-
elements.runStatus.textContent = runStatusCopy(run?.status || "idle");
|
|
376
|
-
elements.metricAccept.textContent =
|
|
377
|
-
elements.metricFirst.textContent =
|
|
378
|
-
elements.metricResponse.textContent =
|
|
381
|
+
elements.runStatus.textContent = run?.completion === "no_reply" && run.status === "completed" ? "未观察到回复" : runStatusCopy(run?.status || "idle");
|
|
382
|
+
elements.metricAccept.textContent = formatElapsedSeconds(run?.acceptLatencyMs);
|
|
383
|
+
elements.metricFirst.textContent = formatElapsedSeconds(run?.firstTextLatencyMs);
|
|
384
|
+
elements.metricResponse.textContent = run?.completion === "no_reply" ? "未观察到回复" : formatElapsedSeconds(replyEndLatency(run));
|
|
379
385
|
elements.metricMessages.textContent = state.simulatorLoaded ? String(state.simulator.messages?.length || 0) : "读取中";
|
|
380
386
|
renderOverview(run);
|
|
381
387
|
renderTimeline(run);
|
|
@@ -405,6 +411,18 @@
|
|
|
405
411
|
}
|
|
406
412
|
}
|
|
407
413
|
|
|
414
|
+
function formatElapsedSeconds(value) {
|
|
415
|
+
return Number.isFinite(value) ? `${Number((value / 1000).toFixed(3))} 秒` : "未记录";
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function replyEndLatency(run) {
|
|
419
|
+
if (run?.status !== "completed") return undefined;
|
|
420
|
+
// updatedAt records Preview observing the terminal state. responseTimeMs and
|
|
421
|
+
// completedAt use the platform message timestamp and are not reply-end timings.
|
|
422
|
+
const elapsed = Date.parse(run.updatedAt) - Date.parse(run.startedAt);
|
|
423
|
+
return Number.isFinite(elapsed) && elapsed >= 0 ? elapsed : undefined;
|
|
424
|
+
}
|
|
425
|
+
|
|
408
426
|
function renderOverview(run) {
|
|
409
427
|
elements.overviewEmpty.hidden = Boolean(run);
|
|
410
428
|
elements.overviewContent.hidden = !run;
|
|
@@ -421,16 +439,19 @@
|
|
|
421
439
|
["消息已提交", "done"],
|
|
422
440
|
["等待回复", hasText || finished ? "done" : "active"],
|
|
423
441
|
[finished && !hasText ? "未记录流式输出" : "生成回复", hasText ? (finished ? "done" : "active") : finished ? "skipped" : "waiting"],
|
|
424
|
-
[run.status === "failed" ? "执行失败" : run.status === "cancelled" ? "已停止" : noReply ? "
|
|
442
|
+
[run.status === "failed" ? "执行失败" : run.status === "cancelled" ? "已停止" : noReply ? "未观察到回复" : "完成", finished ? (interrupted ? "error" : "done") : "waiting"],
|
|
425
443
|
];
|
|
426
444
|
if (interrupted) stages.forEach(stage => { if (stage[1] === "active") stage[1] = "error"; });
|
|
427
445
|
for (const [label, status] of stages) progress.append(node("li", { "data-stage": status }, label));
|
|
428
446
|
}
|
|
429
447
|
clear(elements.waterfall);
|
|
430
448
|
// Only report observed timings. A finished run with missing telemetry never keeps counting.
|
|
431
|
-
const timingRows = [["
|
|
449
|
+
const timingRows = [["消息被接收", run.acceptLatencyMs], ["首字耗时", run.firstTextLatencyMs], ["回复结束", noReply ? undefined : replyEndLatency(run)]];
|
|
432
450
|
const maximum = Math.max(1, ...timingRows.map(([,value]) => Number.isFinite(value) ? value : 0));
|
|
433
451
|
elements.waterfall.hidden = !timingRows.some(([,value]) => Number.isFinite(value));
|
|
452
|
+
const timingHeading = node("div", { class: "timing-heading" });
|
|
453
|
+
timingHeading.append(node("span", {}, "起点:Preview 发起发送(0 秒)"), node("span", {}, "累计时间,不可相加"));
|
|
454
|
+
elements.waterfall.append(timingHeading);
|
|
434
455
|
for (const [label, duration] of timingRows) {
|
|
435
456
|
if (!Number.isFinite(duration)) continue;
|
|
436
457
|
const row = node("div", { class: "waterfall-row" });
|
|
@@ -439,26 +460,29 @@
|
|
|
439
460
|
const fill = node("span", { class: "waterfall-fill" });
|
|
440
461
|
fill.style.transform = `scaleX(${duration / maximum})`;
|
|
441
462
|
track.append(fill);
|
|
442
|
-
row.append(track, node("strong", {},
|
|
463
|
+
row.append(track, node("strong", {}, `发送后 ${formatElapsedSeconds(duration)}`));
|
|
443
464
|
elements.waterfall.append(row);
|
|
444
465
|
}
|
|
445
466
|
elements.runSummaryCopy.textContent = run.status === "failed"
|
|
446
|
-
? "
|
|
467
|
+
? "本轮执行失败。请查看运行日志中的错误详情。"
|
|
447
468
|
: run.status === "cancelled" ? "本轮已停止。已完成的平台写入不会自动撤销。"
|
|
448
|
-
: noReply ? "
|
|
449
|
-
: run.status === "completed" ? (measuredFirstText ?
|
|
469
|
+
: noReply ? "回复状态已结束,未观察到新的可见回复;不据此判断执行是否成功。"
|
|
470
|
+
: run.status === "completed" ? (measuredFirstText ? "" : "本轮未记录首字耗时。")
|
|
450
471
|
: run.status === "cancel_requested" ? "正在停止,结束后可以继续发送消息。"
|
|
451
472
|
: hasText ? "正在生成回复,右侧同步展示输出。" : "消息已提交,等待搭子开始回复。";
|
|
473
|
+
elements.runSummaryCopy.hidden = !elements.runSummaryCopy.textContent;
|
|
452
474
|
}
|
|
453
475
|
|
|
454
476
|
let timelineSignature = "", contextSignature = "", protocolSignature = "";
|
|
455
477
|
const expandedLogs = new Map();
|
|
478
|
+
const diagnostics = window.createPreviewDiagnostics?.({ node, formatTime, formatDuration, toast, showError });
|
|
479
|
+
diagnostics?.update(state.debug);
|
|
456
480
|
function renderTimeline(run) {
|
|
457
481
|
const signature = JSON.stringify([run?.runId, state.debug.events]);
|
|
458
482
|
if (signature === timelineSignature) return;
|
|
459
483
|
timelineSignature = signature;
|
|
460
484
|
clear(elements.timelineList);
|
|
461
|
-
const events = (state.debug.events || []).filter((event) => !run || event.runId === run.runId);
|
|
485
|
+
const events = (state.debug.events || []).filter((event) => event.type !== "run.output.delta" && (!run || event.runId === run.runId));
|
|
462
486
|
if (!events.length) {
|
|
463
487
|
elements.timelineList.append(node("li", { class: "timeline-empty" }, run ? "这个本地回合暂无可见事件。" : "发一条消息后,执行事件会出现在这里。"));
|
|
464
488
|
return;
|
|
@@ -496,6 +520,7 @@
|
|
|
496
520
|
}
|
|
497
521
|
|
|
498
522
|
function renderProtocol() {
|
|
523
|
+
if (diagnostics) { diagnostics.update(state.debug); return; }
|
|
499
524
|
const signature = JSON.stringify(state.debug.events);
|
|
500
525
|
if (signature === protocolSignature) return;
|
|
501
526
|
protocolSignature = signature;
|
|
@@ -536,19 +561,26 @@
|
|
|
536
561
|
}
|
|
537
562
|
|
|
538
563
|
const widgets = window.createSimulatorWidgets?.({ api, avatar: () => messageAvatar("assistant") });
|
|
539
|
-
function renderWidgets() { widgets?.update({ active: state.dev.phase === "ready" && !state.resetting, conversationRevision: state.simulator.conversationRevision, devRevision: state.dev.revision }); }
|
|
564
|
+
function renderWidgets() { widgets?.update({ reloading: ["reloading", "reload_failed"].includes(state.dev.phase), active: state.dev.phase === "ready" && !state.resetting, conversationRevision: state.simulator.conversationRevision, devRevision: state.dev.revision }); }
|
|
540
565
|
|
|
541
566
|
let messageSignature = "";
|
|
542
567
|
let followMessages = true;
|
|
543
|
-
let liveReply, liveReplyText = "";
|
|
568
|
+
let liveReply, liveReplyBubble, liveReplyText = "";
|
|
544
569
|
function renderLiveReply() {
|
|
545
570
|
if (!liveReply) return;
|
|
546
571
|
const text = state.simulator.streaming?.runId === state.simulator.activeRun?.runId ? state.simulator.streaming?.text || "" : "";
|
|
547
572
|
if (text === liveReplyText) return;
|
|
548
573
|
liveReplyText = text;
|
|
549
|
-
|
|
550
|
-
|
|
574
|
+
if (text) {
|
|
575
|
+
if (!liveReplyBubble) {
|
|
576
|
+
clear(liveReply);
|
|
577
|
+
liveReplyBubble = node("div", { class: "message-bubble", "aria-label": "搭子正在回复" });
|
|
578
|
+
liveReplyBubble.append(document.createTextNode(text));
|
|
579
|
+
liveReply.append(liveReplyBubble);
|
|
580
|
+
} else liveReplyBubble.firstChild.data = text;
|
|
581
|
+
}
|
|
551
582
|
else {
|
|
583
|
+
clear(liveReply); liveReplyBubble = undefined;
|
|
552
584
|
const indicator = node("div", { class: "replying-indicator", "aria-label": "搭子正在准备回复" });
|
|
553
585
|
indicator.append(node("i"), node("i"), node("i")); liveReply.append(indicator);
|
|
554
586
|
}
|
|
@@ -568,7 +600,7 @@
|
|
|
568
600
|
messageSignature = signature;
|
|
569
601
|
const previousTop = elements.chatMessages.scrollTop;
|
|
570
602
|
clear(elements.chatMessages);
|
|
571
|
-
liveReply = undefined; liveReplyText = "";
|
|
603
|
+
liveReply = undefined; liveReplyBubble = undefined; liveReplyText = "";
|
|
572
604
|
const messages = state.simulator.messages || [];
|
|
573
605
|
if (!messages.length && !state.simulator.activeRun) {
|
|
574
606
|
elements.chatMessages.append(node("div", { class: "chat-empty", "aria-label": "暂无消息" }, hosted ? (state.dev.phase === "ready" ? "发一条消息,开始体验" : "启动 DEV 后,开始对话") : ""));
|
|
@@ -645,9 +677,14 @@
|
|
|
645
677
|
}
|
|
646
678
|
|
|
647
679
|
function syncPolling() {
|
|
680
|
+
const interval = liveEventsActive ? 30000 : 1200;
|
|
681
|
+
if (state.polling !== undefined && pollingInterval !== interval) {
|
|
682
|
+
window.clearInterval(state.polling); state.polling = undefined;
|
|
683
|
+
}
|
|
648
684
|
if (connectionReady && state.dev.phase === "ready" && state.polling === undefined) {
|
|
649
685
|
void refreshSimulator();
|
|
650
|
-
|
|
686
|
+
pollingInterval = interval;
|
|
687
|
+
state.polling = window.setInterval(() => void refreshSimulator(), interval);
|
|
651
688
|
} else if ((!connectionReady || state.dev.phase !== "ready") && state.polling !== undefined) {
|
|
652
689
|
window.clearInterval(state.polling);
|
|
653
690
|
state.polling = undefined;
|
|
@@ -657,7 +694,7 @@
|
|
|
657
694
|
async function controlDev() {
|
|
658
695
|
if (!connectionReady || state.dev.phase === "starting" || state.dev.phase === "stopping") return;
|
|
659
696
|
if (devAction) return;
|
|
660
|
-
devAction = state.dev.phase
|
|
697
|
+
devAction = ["ready", "reloading", "reload_failed"].includes(state.dev.phase) ? "stop" : "start";
|
|
661
698
|
elements.errorBanner.hidden = true;
|
|
662
699
|
renderDev();
|
|
663
700
|
try {
|
|
@@ -675,7 +712,7 @@
|
|
|
675
712
|
}
|
|
676
713
|
|
|
677
714
|
function updateSendButton() {
|
|
678
|
-
const ready = connectionReady && !devAction && state.simulatorLoaded && state.dev.phase === "ready" && !state.
|
|
715
|
+
const ready = connectionReady && !devAction && state.simulatorLoaded && state.dev.phase === "ready" && !state.resetting && !state.sending;
|
|
679
716
|
elements.sendMessage.disabled = !ready || !elements.messageInput.value.trim();
|
|
680
717
|
}
|
|
681
718
|
|
|
@@ -684,7 +721,7 @@
|
|
|
684
721
|
async function sendMessage(event) {
|
|
685
722
|
event.preventDefault();
|
|
686
723
|
const text = elements.messageInput.value;
|
|
687
|
-
if (!text.trim() || !state.simulatorLoaded || devAction || state.dev.phase !== "ready" || state.
|
|
724
|
+
if (!text.trim() || !state.simulatorLoaded || devAction || state.dev.phase !== "ready" || state.resetting || state.sending) return;
|
|
688
725
|
state.sending = true;
|
|
689
726
|
elements.errorBanner.hidden = true;
|
|
690
727
|
followLatestMessages();
|
|
@@ -706,10 +743,10 @@
|
|
|
706
743
|
pendingSend = undefined;
|
|
707
744
|
elements.messageInput.value = "";
|
|
708
745
|
resizeComposer();
|
|
709
|
-
state.simulator.activeRun = { runId: accepted.runId, status: "pending" };
|
|
746
|
+
if (state.simulator.latestRun?.runId !== accepted.runId) state.simulator.activeRun = { runId: accepted.runId, status: "pending" };
|
|
710
747
|
renderDev();
|
|
711
748
|
renderMessages();
|
|
712
|
-
|
|
749
|
+
void refreshSimulator();
|
|
713
750
|
} catch (cause) {
|
|
714
751
|
showError(cause);
|
|
715
752
|
} finally {
|
|
@@ -774,6 +811,7 @@
|
|
|
774
811
|
}
|
|
775
812
|
|
|
776
813
|
async function copyProtocol() {
|
|
814
|
+
if (diagnostics) return diagnostics.copyLogs();
|
|
777
815
|
const text = (state.debug.events || []).map((event) => [
|
|
778
816
|
event.timestamp,
|
|
779
817
|
event.channel,
|
|
@@ -792,6 +830,8 @@
|
|
|
792
830
|
|
|
793
831
|
function applySimulatorState(next) {
|
|
794
832
|
if ((next.conversationRevision || 0) < (state.simulator.conversationRevision || 0)) return false;
|
|
833
|
+
if (Number.isFinite(next.stateRevision) && Number.isFinite(state.simulator.stateRevision)
|
|
834
|
+
&& next.stateRevision < state.simulator.stateRevision) return false;
|
|
795
835
|
state.simulator = next;
|
|
796
836
|
state.simulatorLoaded = true;
|
|
797
837
|
return true;
|
|
@@ -831,8 +871,20 @@
|
|
|
831
871
|
}
|
|
832
872
|
|
|
833
873
|
function connectEvents() {
|
|
834
|
-
|
|
874
|
+
if (eventSource && eventSourceInstance === connectionInstance) return;
|
|
875
|
+
closeEvents();
|
|
876
|
+
const events = new EventSource(previewApiUrl("/api/events"));
|
|
877
|
+
eventSource = events; eventSourceInstance = connectionInstance;
|
|
878
|
+
events.addEventListener("open", () => {
|
|
879
|
+
if (eventSource !== events) return;
|
|
880
|
+
liveEventsActive = true; syncPolling();
|
|
881
|
+
});
|
|
882
|
+
events.addEventListener("error", () => {
|
|
883
|
+
if (eventSource !== events) return;
|
|
884
|
+
liveEventsActive = false; syncPolling();
|
|
885
|
+
});
|
|
835
886
|
events.addEventListener("dev.state", (event) => {
|
|
887
|
+
if (eventSource !== events) return;
|
|
836
888
|
try {
|
|
837
889
|
if (applyDevState(JSON.parse(event.data))) { renderDev(); renderWidgets(); }
|
|
838
890
|
} catch {
|
|
@@ -840,12 +892,15 @@
|
|
|
840
892
|
}
|
|
841
893
|
});
|
|
842
894
|
events.addEventListener("simulator.state", (event) => {
|
|
895
|
+
if (eventSource !== events || state.resetting) return;
|
|
843
896
|
try {
|
|
844
897
|
const value = JSON.parse(event.data);
|
|
898
|
+
const before = JSON.stringify([state.simulator.activeRun, Boolean(state.simulator.streaming?.text), state.simulatorLoaded]);
|
|
845
899
|
if (!applySimulatorState(value.state)) return;
|
|
846
|
-
state.debug = value.debug;
|
|
847
|
-
|
|
848
|
-
|
|
900
|
+
if (value.debug) state.debug = value.debug;
|
|
901
|
+
const after = JSON.stringify([state.simulator.activeRun, Boolean(state.simulator.streaming?.text), state.simulatorLoaded]);
|
|
902
|
+
if (before !== after) renderDev();
|
|
903
|
+
if (value.debug) renderDebug();
|
|
849
904
|
renderMessages();
|
|
850
905
|
} catch {
|
|
851
906
|
// Polling remains the reconciliation path for a malformed or missed local event.
|
|
@@ -853,6 +908,11 @@
|
|
|
853
908
|
});
|
|
854
909
|
}
|
|
855
910
|
|
|
911
|
+
function closeEvents() {
|
|
912
|
+
eventSource?.close(); eventSource = undefined; eventSourceInstance = ""; liveEventsActive = false;
|
|
913
|
+
}
|
|
914
|
+
window.addEventListener("pagehide", closeEvents);
|
|
915
|
+
|
|
856
916
|
async function boot() {
|
|
857
917
|
if (hosted && !connectionReady) { void refreshConnection(); return; }
|
|
858
918
|
try {
|
|
@@ -877,6 +937,7 @@
|
|
|
877
937
|
}
|
|
878
938
|
|
|
879
939
|
function disconnected(message = "未连接本地工程") {
|
|
940
|
+
closeEvents();
|
|
880
941
|
connectionReady = false;
|
|
881
942
|
state.dev = { phase: "idle", revision: -1 };
|
|
882
943
|
state.simulatorRequest++;
|
|
@@ -898,6 +959,7 @@
|
|
|
898
959
|
$("#copy-connect-command").disabled = false;
|
|
899
960
|
if (!value.connected) { disconnected(); return; }
|
|
900
961
|
const changed = connectionInstance !== value.instanceId;
|
|
962
|
+
if (changed) closeEvents();
|
|
901
963
|
const newlyConnected = !connectionReady || changed;
|
|
902
964
|
connectionInstance = value.instanceId;
|
|
903
965
|
connectionReady = true;
|
|
@@ -918,6 +980,7 @@
|
|
|
918
980
|
renderDev();
|
|
919
981
|
await boot();
|
|
920
982
|
}
|
|
983
|
+
if (value.liveEvents) connectEvents();
|
|
921
984
|
} catch (cause) {
|
|
922
985
|
const message = cause.message || "连接已中断,正在重试…";
|
|
923
986
|
connectionChecked = true; connectionFailure = message;
|
|
@@ -932,7 +995,8 @@
|
|
|
932
995
|
devPollBusy = true;
|
|
933
996
|
try {
|
|
934
997
|
const [dev, debug] = await Promise.all([api("/api/dev"), api("/api/debug")]);
|
|
935
|
-
applyDevState(dev.state)
|
|
998
|
+
if (applyDevState(dev.state)) renderDev();
|
|
999
|
+
state.debug = debug.debug; renderDebug();
|
|
936
1000
|
} catch (cause) {
|
|
937
1001
|
// Polling conflicts are connection transitions, already explained by the workbench state.
|
|
938
1002
|
if (!connectionReady || cause?.status === 409) void refreshConnection();
|