@eclipse-docks/extension-ai-system 0.7.141 → 0.8.1

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.
Files changed (42) hide show
  1. package/README.md +2 -2
  2. package/dist/api.js +1 -1
  3. package/dist/{cognitive-extension-CG_d39K5.js → cognitive-extension-7_ZMUCGD.js} +358 -92
  4. package/dist/cognitive-extension-7_ZMUCGD.js.map +1 -0
  5. package/dist/index.js +1 -1
  6. package/dist/runtime/abort.d.ts +5 -0
  7. package/dist/runtime/abort.d.ts.map +1 -0
  8. package/dist/runtime/attachment-paths.d.ts +17 -0
  9. package/dist/runtime/attachment-paths.d.ts.map +1 -0
  10. package/dist/runtime/attachments.d.ts +5 -5
  11. package/dist/runtime/attachments.d.ts.map +1 -1
  12. package/dist/runtime/cognitive-runtime.d.ts +7 -3
  13. package/dist/runtime/cognitive-runtime.d.ts.map +1 -1
  14. package/dist/runtime/index.d.ts +2 -0
  15. package/dist/runtime/index.d.ts.map +1 -1
  16. package/dist/runtime/models/openai-compatible.d.ts.map +1 -1
  17. package/dist/runtime/session-workspace.d.ts +20 -0
  18. package/dist/runtime/session-workspace.d.ts.map +1 -0
  19. package/dist/runtime/specialists/index.d.ts.map +1 -1
  20. package/dist/runtime/specialists/loop.d.ts.map +1 -1
  21. package/dist/runtime/tools/command-adapter.d.ts.map +1 -1
  22. package/dist/runtime/tools/index.d.ts +0 -1
  23. package/dist/runtime/tools/index.d.ts.map +1 -1
  24. package/dist/runtime/tools/workspace-adapter.d.ts +1 -1
  25. package/dist/runtime/tools/workspace-adapter.d.ts.map +1 -1
  26. package/dist/runtime/tools/workspace-path-qualify.d.ts +22 -0
  27. package/dist/runtime/tools/workspace-path-qualify.d.ts.map +1 -0
  28. package/dist/runtime/tools/workspace-path.d.ts +17 -0
  29. package/dist/runtime/tools/workspace-path.d.ts.map +1 -0
  30. package/dist/{runtime-DfNGzEjU.js → runtime-By5PP2BC.js} +696 -384
  31. package/dist/runtime-By5PP2BC.js.map +1 -0
  32. package/dist/view/busy-status.d.ts +12 -0
  33. package/dist/view/busy-status.d.ts.map +1 -0
  34. package/dist/view/cognitive-settings-editor.d.ts +0 -1
  35. package/dist/view/cognitive-settings-editor.d.ts.map +1 -1
  36. package/dist/view/cognitive-view.d.ts +21 -0
  37. package/dist/view/cognitive-view.d.ts.map +1 -1
  38. package/package.json +1 -1
  39. package/dist/cognitive-extension-CG_d39K5.js.map +0 -1
  40. package/dist/runtime/tools/attachment-adapter.d.ts +0 -6
  41. package/dist/runtime/tools/attachment-adapter.d.ts.map +0 -1
  42. package/dist/runtime-DfNGzEjU.js.map +0 -1
@@ -1,20 +1,69 @@
1
- import { _ as AI_SETTINGS_KEY, a as formatContextPercent, b as TOPIC, c as readPromptAttachmentFromFile, d as defaultProvidersSettings, g as AI_SETTINGS_EDITOR_ID, i as formatBucketDetail, m as detectEffortCapability, n as cognitiveRuntime, o as formatTokenCount, p as parseModelIds, r as estimateSessionContextUsage, s as ATTACHMENT_FILE_ACCEPT, u as fetchOpenAICompatibleModels, v as AI_VIEW_NAME, y as OPEN_AI_SETTINGS_COMMAND } from "./runtime-DfNGzEjU.js";
1
+ import { _ as AI_SETTINGS_KEY, a as formatContextPercent, b as TOPIC, c as readPromptAttachmentFromFile, d as defaultProvidersSettings, g as AI_SETTINGS_EDITOR_ID, i as formatBucketDetail, m as detectEffortCapability, n as cognitiveRuntime, o as formatTokenCount, p as parseModelIds, r as estimateSessionContextUsage, s as ATTACHMENT_FILE_ACCEPT, u as fetchOpenAICompatibleModels, v as AI_VIEW_NAME, y as OPEN_AI_SETTINGS_COMMAND } from "./runtime-By5PP2BC.js";
2
2
  import { DocksPart, SIDEBAR_AUXILIARY, confirmDialog, contributionRegistry, editorRegistry, registerAll, rootContext, toastError, toastInfo } from "@eclipse-docks/core";
3
3
  import { css, customElement, html, nothing, property, query, state } from "@eclipse-docks/core/externals/lit";
4
4
  import "@eclipse-docks/core/externals/webawesome";
5
5
  import _decorate from "@oxc-project/runtime/helpers/decorate";
6
+ //#region src/view/busy-status.ts
7
+ /** Shown immediately when waiting on a model turn. */
8
+ var INITIAL_BUSY_STATUS = "Thinking…";
9
+ /**
10
+ * Rotating wait copy (Cursor / Copilot style). Picked at random after the initial message.
11
+ * Keep these light and short — they appear in the composer while busy.
12
+ */
13
+ var BUSY_STATUS_MESSAGES = [
14
+ "Bribing the hamster…",
15
+ "Consulting the rubber duck…",
16
+ "Untangling the spaghetti…",
17
+ "Herding tokens…",
18
+ "Warming up the crystals…",
19
+ "Counting electric sheep…",
20
+ "Negotiating with the compiler…",
21
+ "Teaching frogs to code…",
22
+ "Aligning the bits…",
23
+ "Feeding the GPU snacks…",
24
+ "Looking under the couch…",
25
+ "Summoning a specialist…",
26
+ "Checking the manual twice…",
27
+ "Reticulating splines…",
28
+ "Sharpening pencils…",
29
+ "Pedaling harder…",
30
+ "Buffering brilliance…",
31
+ "Reversing the polarity…",
32
+ "Convincing the bits to cooperate…",
33
+ "Dusting off the playbook…",
34
+ "Whispering to the model…",
35
+ "Calibrating optimism…",
36
+ "Unfolding the roadmap…",
37
+ "Polishing ones and zeros…",
38
+ "Asking politely…"
39
+ ];
40
+ var ROTATE_MS_MIN = 3e3;
41
+ /** Random delay until the next busy-status line (3–12s). */
42
+ function busyStatusRotateMs() {
43
+ return ROTATE_MS_MIN + Math.floor(Math.random() * 9001);
44
+ }
45
+ /** Next status, avoiding an immediate repeat of `current`. */
46
+ function nextBusyStatus(current) {
47
+ const pool = BUSY_STATUS_MESSAGES.filter((m) => m !== current);
48
+ if (!pool.length) return INITIAL_BUSY_STATUS;
49
+ return pool[Math.floor(Math.random() * pool.length)];
50
+ }
51
+ //#endregion
6
52
  //#region src/view/cognitive-view.ts
7
53
  var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
8
54
  constructor(..._args) {
9
55
  super(..._args);
10
56
  this.scrollMode = "none";
57
+ this.composerPasteBound = false;
11
58
  this.sessions = [];
12
59
  this.focusedId = null;
13
60
  this.draft = "";
14
61
  this.pendingAttachmentIds = [];
15
62
  this.dragOver = false;
16
63
  this.previewAttachmentId = null;
64
+ this.toolDetail = null;
17
65
  this.busy = false;
66
+ this.busyStatus = INITIAL_BUSY_STATUS;
18
67
  this.pendingPermission = null;
19
68
  this.policyMode = "auto";
20
69
  this.optimizeFor = "balance";
@@ -26,6 +75,13 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
26
75
  this.historyOpen = false;
27
76
  this.historyQuery = "";
28
77
  this.unsubs = [];
78
+ this.promptController = null;
79
+ this.nativePasteHandler = (e) => void this.onComposerPaste(e);
80
+ this.cancelKeyHandler = (e) => {
81
+ if (!this.busy || e.key !== "Escape") return;
82
+ e.preventDefault();
83
+ this.cancelTurn();
84
+ };
29
85
  }
30
86
  static {
31
87
  this.styles = css`
@@ -490,8 +546,44 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
490
546
  color: var(--wa-color-text-quiet);
491
547
  margin-bottom: var(--wa-space-2xs);
492
548
  }
549
+
550
+ .tool-row {
551
+ display: flex;
552
+ align-items: center;
553
+ gap: var(--wa-space-2xs);
554
+ flex-wrap: wrap;
555
+ }
556
+
557
+ .tool-row .tool-heading {
558
+ margin-bottom: 0;
559
+ flex: 1;
560
+ min-width: 0;
561
+ }
562
+
563
+ .tool-detail-body {
564
+ max-height: min(70vh, 36rem);
565
+ overflow: auto;
566
+ }
567
+
568
+ .tool-detail-body pre {
569
+ margin: 0;
570
+ font-family: var(--wa-font-family-code, var(--wa-font-mono));
571
+ font-size: var(--wa-font-size-xs);
572
+ white-space: pre-wrap;
573
+ word-break: break-word;
574
+ }
493
575
  `;
494
576
  }
577
+ updated() {
578
+ this.bindComposerPaste();
579
+ }
580
+ bindComposerPaste() {
581
+ if (this.composerPasteBound) return;
582
+ const native = this.textarea?.shadowRoot?.querySelector("textarea");
583
+ if (!native) return;
584
+ native.addEventListener("paste", this.nativePasteHandler);
585
+ this.composerPasteBound = true;
586
+ }
495
587
  doBeforeUI() {
496
588
  cognitiveRuntime.start();
497
589
  this.syncSessions();
@@ -499,14 +591,13 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
499
591
  this.approvalMode = cognitiveRuntime.getApprovalMode();
500
592
  cognitiveRuntime.whenReady().then(() => {
501
593
  this.syncSessions();
502
- this.syncModels();
503
- this.approvalMode = cognitiveRuntime.getApprovalMode();
594
+ this.restoreRoutingFromRuntime();
504
595
  this.scheduleScrollToBottom();
505
596
  });
506
597
  this.unsubs.push(cognitiveRuntime.events.subscribe(TOPIC.sessionChanged, () => {
507
598
  this.syncSessions();
508
599
  this.scheduleScrollToBottom();
509
- }), cognitiveRuntime.events.subscribe(TOPIC.providersChanged, () => this.syncModels()), cognitiveRuntime.events.subscribe(TOPIC.approvalModeChanged, (mode) => {
600
+ }), cognitiveRuntime.events.subscribe(TOPIC.providersChanged, () => this.onProvidersChanged()), cognitiveRuntime.events.subscribe(TOPIC.approvalModeChanged, (mode) => {
510
601
  this.approvalMode = mode;
511
602
  }), cognitiveRuntime.events.subscribe(TOPIC.permissionAsked, (payload) => {
512
603
  this.pendingPermission = payload;
@@ -525,9 +616,33 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
525
616
  doClose() {
526
617
  if (this.scrollDebounceTimer) clearTimeout(this.scrollDebounceTimer);
527
618
  this.scrollDebounceTimer = void 0;
619
+ this.cancelTurn();
620
+ this.stopBusyStatus();
621
+ (this.textarea?.shadowRoot?.querySelector("textarea"))?.removeEventListener("paste", this.nativePasteHandler);
622
+ this.composerPasteBound = false;
528
623
  for (const u of this.unsubs) u();
529
624
  this.unsubs = [];
530
625
  }
626
+ startBusyStatus() {
627
+ this.stopBusyStatus();
628
+ this.busyStatus = INITIAL_BUSY_STATUS;
629
+ this.setComposerText(this.busyStatus);
630
+ window.addEventListener("keydown", this.cancelKeyHandler);
631
+ const schedule = () => {
632
+ this.busyStatusTimer = setTimeout(() => {
633
+ this.busyStatus = nextBusyStatus(this.busyStatus);
634
+ this.setComposerText(this.busyStatus);
635
+ schedule();
636
+ }, busyStatusRotateMs());
637
+ };
638
+ schedule();
639
+ }
640
+ stopBusyStatus() {
641
+ if (this.busyStatusTimer) clearTimeout(this.busyStatusTimer);
642
+ this.busyStatusTimer = void 0;
643
+ this.busyStatus = INITIAL_BUSY_STATUS;
644
+ window.removeEventListener("keydown", this.cancelKeyHandler);
645
+ }
531
646
  syncSessions() {
532
647
  const byRecency = cognitiveRuntime.sessions.list();
533
648
  this.sessions = [...byRecency].sort((a, b) => a.createdAt - b.createdAt);
@@ -558,11 +673,10 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
558
673
  deleteSession(sessionId) {
559
674
  if (this.sessions.length <= 1) {
560
675
  cognitiveRuntime.sessions.createSession({ title: "Chat" });
561
- cognitiveRuntime.sessions.remove(sessionId);
562
- } else cognitiveRuntime.sessions.remove(sessionId);
676
+ cognitiveRuntime.removeSession(sessionId);
677
+ } else cognitiveRuntime.removeSession(sessionId);
563
678
  this.streamingParts = [];
564
679
  this.syncSessions();
565
- cognitiveRuntime.commitSessions();
566
680
  }
567
681
  openSession(sessionId) {
568
682
  if (!cognitiveRuntime.sessions.focus(sessionId)) return;
@@ -598,6 +712,31 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
598
712
  this.modelIds = cognitiveRuntime.listConfiguredModels();
599
713
  if (!this.manualModel || !this.modelIds.includes(this.manualModel)) this.manualModel = this.modelIds[0] ?? cognitiveRuntime.defaultModelId();
600
714
  }
715
+ /** Mirror persisted runtime policy into the gear menu after hydrate. */
716
+ restoreRoutingFromRuntime() {
717
+ this.syncModels();
718
+ const policy = cognitiveRuntime.getModelPolicy();
719
+ if (policy.mode === "auto") {
720
+ this.policyMode = "auto";
721
+ this.optimizeFor = policy.optimizeFor;
722
+ } else {
723
+ this.policyMode = "manual";
724
+ this.manualModel = policy.model;
725
+ this.manualEffort = policy.effort ?? "auto";
726
+ if (!this.manualModel || !this.modelIds.includes(this.manualModel)) {
727
+ this.manualModel = this.modelIds[0] ?? cognitiveRuntime.defaultModelId();
728
+ this.applyPolicy();
729
+ }
730
+ }
731
+ this.approvalMode = cognitiveRuntime.getApprovalMode();
732
+ }
733
+ onProvidersChanged() {
734
+ this.syncModels();
735
+ if (this.policyMode !== "manual") return;
736
+ if (this.manualModel && this.modelIds.includes(this.manualModel)) return;
737
+ this.manualModel = this.modelIds[0] ?? cognitiveRuntime.defaultModelId();
738
+ this.applyPolicy();
739
+ }
601
740
  effortChoices() {
602
741
  const cap = detectEffortCapability(this.manualModel || cognitiveRuntime.defaultModelId());
603
742
  if (!cap.supported) return ["auto", "none"];
@@ -631,6 +770,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
631
770
  if (optimize === "cost" || optimize === "balance" || optimize === "intelligence") {
632
771
  this.policyMode = "auto";
633
772
  this.optimizeFor = optimize;
773
+ this.applyPolicy();
634
774
  }
635
775
  return;
636
776
  }
@@ -639,6 +779,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
639
779
  if (!model) return;
640
780
  this.policyMode = "manual";
641
781
  this.manualModel = model;
782
+ this.applyPolicy();
642
783
  }
643
784
  }
644
785
  applyPolicy() {
@@ -652,12 +793,15 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
652
793
  };
653
794
  cognitiveRuntime.setModelPolicy(policy);
654
795
  }
796
+ async setComposerText(value) {
797
+ this.draft = value;
798
+ await this.updateComplete;
799
+ if (this.textarea) this.textarea.value = value;
800
+ }
655
801
  async clearComposer() {
656
- this.draft = "";
657
802
  this.pendingAttachmentIds = [];
658
- await this.updateComplete;
659
- if (this.textarea) this.textarea.value = "";
660
803
  if (this.fileInput) this.fileInput.value = "";
804
+ await this.setComposerText("");
661
805
  }
662
806
  sessionAttachments() {
663
807
  return this.focusedSession()?.attachments ?? [];
@@ -699,7 +843,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
699
843
  }
700
844
  accepted.push(result.attachment);
701
845
  }
702
- const stored = cognitiveRuntime.addAttachments(sessionId, accepted);
846
+ const stored = await cognitiveRuntime.addAttachments(sessionId, accepted);
703
847
  if (stored.length) this.pendingAttachmentIds = [...this.pendingAttachmentIds, ...stored.map((a) => a.id).filter((id) => !this.pendingAttachmentIds.includes(id))];
704
848
  this.syncSessions();
705
849
  }
@@ -724,22 +868,47 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
724
868
  this.dragOver = false;
725
869
  if (e.dataTransfer?.files?.length) await this.addFiles(e.dataTransfer.files);
726
870
  }
871
+ /** Attach clipboard files/images (e.g. Ctrl+V screenshot) when focused in the prompt. */
872
+ async onComposerPaste(e) {
873
+ if (this.busy) {
874
+ e.preventDefault();
875
+ return;
876
+ }
877
+ const files = filesFromClipboard(e.clipboardData);
878
+ if (!files.length) return;
879
+ e.preventDefault();
880
+ await this.addFiles(files);
881
+ }
882
+ cancelTurn() {
883
+ if (!this.busy && !this.promptController) return;
884
+ this.promptController?.abort();
885
+ if (this.pendingPermission) this.resolvePermission("deny");
886
+ }
727
887
  async send() {
728
888
  const text = this.draft.trim();
729
889
  const attachmentIds = [...this.pendingAttachmentIds];
730
890
  if (!text && !attachmentIds.length || this.busy) return;
731
891
  this.applyPolicy();
732
- await this.clearComposer();
892
+ this.pendingAttachmentIds = [];
893
+ if (this.fileInput) this.fileInput.value = "";
894
+ const controller = new AbortController();
895
+ this.promptController = controller;
733
896
  this.busy = true;
734
897
  this.streamingParts = [];
898
+ this.startBusyStatus();
735
899
  try {
736
900
  await cognitiveRuntime.prompt({
737
901
  sessionId: this.focusedId ?? void 0,
738
902
  text,
739
- attachmentIds
903
+ attachmentIds,
904
+ signal: controller.signal
740
905
  });
741
906
  } finally {
907
+ if (this.promptController === controller) this.promptController = null;
908
+ this.stopBusyStatus();
742
909
  this.busy = false;
910
+ this.streamingParts = [];
911
+ await this.clearComposer();
743
912
  this.syncSessions();
744
913
  this.scheduleScrollToBottom();
745
914
  this.textarea?.focus();
@@ -839,6 +1008,38 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
839
1008
  <wa-button variant="brand" @click=${() => this.closeAttachmentPreview()}>Close</wa-button>
840
1009
  </div>
841
1010
  </wa-dialog>
1011
+ `;
1012
+ }
1013
+ openToolDetail(part) {
1014
+ this.toolDetail = part;
1015
+ }
1016
+ closeToolDetail() {
1017
+ this.toolDetail = null;
1018
+ }
1019
+ renderToolDetailDialog() {
1020
+ const tool = this.toolDetail;
1021
+ if (!tool) return nothing;
1022
+ const label = `${tool.name} · ${tool.status}${tool.durationMs != null ? ` · ${tool.durationMs}ms` : ""}`;
1023
+ const body = JSON.stringify({
1024
+ args: tool.args ?? {},
1025
+ result: tool.result ?? null
1026
+ }, null, 2);
1027
+ return html`
1028
+ <wa-dialog
1029
+ label=${label}
1030
+ open
1031
+ light-dismiss
1032
+ with-footer
1033
+ style="--width: min(44rem, 96vw)"
1034
+ @wa-after-hide=${() => this.closeToolDetail()}
1035
+ >
1036
+ <div class="tool-detail-body">
1037
+ <pre>${body}</pre>
1038
+ </div>
1039
+ <div slot="footer">
1040
+ <wa-button variant="brand" @click=${() => this.closeToolDetail()}>Close</wa-button>
1041
+ </div>
1042
+ </wa-dialog>
842
1043
  `;
843
1044
  }
844
1045
  resolvePermission(action) {
@@ -975,16 +1176,27 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
975
1176
  <div class="composer">
976
1177
  <div class="composer-row">
977
1178
  <wa-textarea
978
- placeholder="Enter your message here..."
1179
+ placeholder=${this.busy ? this.busyStatus : "Enter your message here…"}
979
1180
  size="s"
980
1181
  resize="auto"
981
1182
  rows="1"
982
- .value=${this.draft}
1183
+ .value=${this.busy ? this.busyStatus : this.draft}
983
1184
  ?disabled=${this.busy}
1185
+ ?readonly=${this.busy}
984
1186
  @input=${(e) => {
1187
+ if (this.busy) return;
985
1188
  this.draft = e.target.value;
986
1189
  }}
987
1190
  @keydown=${(e) => {
1191
+ if (this.busy) {
1192
+ if (e.key === "Escape") {
1193
+ e.preventDefault();
1194
+ this.cancelTurn();
1195
+ return;
1196
+ }
1197
+ e.preventDefault();
1198
+ return;
1199
+ }
988
1200
  if (e.key === "Enter" && !e.shiftKey) {
989
1201
  e.preventDefault();
990
1202
  this.send();
@@ -992,6 +1204,17 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
992
1204
  }}
993
1205
  ></wa-textarea>
994
1206
  <div class="composer-actions">
1207
+ ${this.busy ? html`
1208
+ <wa-button
1209
+ size="s"
1210
+ appearance="plain"
1211
+ variant="danger"
1212
+ title="Stop (Esc)"
1213
+ @click=${() => this.cancelTurn()}
1214
+ >
1215
+ <wa-icon name="stop" label="Stop"></wa-icon>
1216
+ </wa-button>
1217
+ ` : nothing}
995
1218
  ${this.renderAttachmentDropdown()}
996
1219
  ${this.renderContextDropdown()}
997
1220
  </div>
@@ -999,6 +1222,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
999
1222
  </div>
1000
1223
  </div>
1001
1224
  ${this.renderAttachmentDialog()}
1225
+ ${this.renderToolDetailDialog()}
1002
1226
  </div>
1003
1227
  `;
1004
1228
  }
@@ -1130,6 +1354,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1130
1354
  }
1131
1355
  if (value.startsWith("effort:")) {
1132
1356
  this.manualEffort = value.slice(7) || "auto";
1357
+ this.applyPolicy();
1133
1358
  return;
1134
1359
  }
1135
1360
  this.onRouterSelect(value);
@@ -1177,7 +1402,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1177
1402
  <wa-dropdown-item
1178
1403
  value=${option.value}
1179
1404
  type="checkbox"
1180
- ?checked=${option.value === selected}
1405
+ .checked=${option.value === selected}
1181
1406
  >
1182
1407
  ${option.label}
1183
1408
  </wa-dropdown-item>
@@ -1189,7 +1414,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1189
1414
  <wa-dropdown-item
1190
1415
  value=${`model:${id}`}
1191
1416
  type="checkbox"
1192
- ?checked=${selected === `model:${id}`}
1417
+ .checked=${selected === `model:${id}`}
1193
1418
  >
1194
1419
  ${id}
1195
1420
  </wa-dropdown-item>
@@ -1202,7 +1427,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1202
1427
  <wa-dropdown-item
1203
1428
  value=${`effort:${level}`}
1204
1429
  type="checkbox"
1205
- ?checked=${this.manualEffort === level}
1430
+ .checked=${this.manualEffort === level}
1206
1431
  >
1207
1432
  ${level}
1208
1433
  </wa-dropdown-item>
@@ -1215,7 +1440,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1215
1440
  <wa-dropdown-item
1216
1441
  value=${`approval:${option.value}`}
1217
1442
  type="checkbox"
1218
- ?checked=${this.approvalMode === option.value}
1443
+ .checked=${this.approvalMode === option.value}
1219
1444
  >
1220
1445
  ${option.label}
1221
1446
  </wa-dropdown-item>
@@ -1268,18 +1493,18 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1268
1493
  return this.renderAssistantMessage(message);
1269
1494
  }
1270
1495
  renderAssistantMessage(message, opts = {}) {
1271
- const textParts = message.parts.filter((p) => p.type === "text");
1272
- const techParts = message.parts.filter((p) => p.type !== "text");
1496
+ const { summaryText, techParts } = splitAssistantPresentation(message.parts);
1273
1497
  const open = opts.streaming === true || techParts.some((p) => (p.type === "reasoning" || p.type === "tool") && p.status === "streaming");
1498
+ const summaryBubble = summaryText.length ? summaryText.map((p) => this.renderTextPart(p)) : html`<div class="bubble">${technicalSummary(techParts)}</div>`;
1274
1499
  const summary = html`
1275
1500
  <div slot="summary" class="msg-summary">
1276
1501
  ${opts.streaming ? html`
1277
1502
  <div class="streaming-meta">
1278
1503
  <wa-spinner style="font-size: 0.85rem"></wa-spinner>
1279
- <span>streaming…</span>
1504
+ <span>${this.busy ? this.busyStatus : "streaming…"}</span>
1280
1505
  </div>
1281
1506
  ` : nothing}
1282
- ${textParts.length ? textParts.map((p) => this.renderTextPart(p)) : html`<div class="bubble">${technicalSummary(techParts)}</div>`}
1507
+ ${summaryBubble}
1283
1508
  </div>
1284
1509
  `;
1285
1510
  if (!techParts.length) return html`
@@ -1287,10 +1512,10 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1287
1512
  ${opts.streaming ? html`
1288
1513
  <div class="streaming-meta">
1289
1514
  <wa-spinner style="font-size: 0.85rem"></wa-spinner>
1290
- <span>streaming…</span>
1515
+ <span>${this.busy ? this.busyStatus : "streaming…"}</span>
1291
1516
  </div>
1292
1517
  ` : nothing}
1293
- ${textParts.map((p) => this.renderTextPart(p))}
1518
+ ${summaryBubble}
1294
1519
  ${message.id !== "streaming" ? this.renderMessageMeta(message) : nothing}
1295
1520
  </div>
1296
1521
  `;
@@ -1329,13 +1554,20 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1329
1554
  `;
1330
1555
  if (part.type === "tool") {
1331
1556
  const heading = `${part.name} · ${part.status}${part.durationMs != null ? ` · ${part.durationMs}ms` : ""}`;
1557
+ const hasDetail = part.args != null || part.result != null || part.status === "error";
1332
1558
  return html`
1333
- <div>
1559
+ <div class="tool-row">
1334
1560
  <div class="tool-heading">${heading}</div>
1335
- <pre class="args">${JSON.stringify({
1336
- args: part.args,
1337
- result: part.result
1338
- }, null, 2)}</pre>
1561
+ ${hasDetail ? html`
1562
+ <wa-button
1563
+ size="s"
1564
+ appearance="plain"
1565
+ variant="neutral"
1566
+ @click=${() => this.openToolDetail(part)}
1567
+ >
1568
+ Details
1569
+ </wa-button>
1570
+ ` : nothing}
1339
1571
  </div>
1340
1572
  `;
1341
1573
  }
@@ -1351,7 +1583,9 @@ _decorate([state()], DocksCognitiveView.prototype, "draft", void 0);
1351
1583
  _decorate([state()], DocksCognitiveView.prototype, "pendingAttachmentIds", void 0);
1352
1584
  _decorate([state()], DocksCognitiveView.prototype, "dragOver", void 0);
1353
1585
  _decorate([state()], DocksCognitiveView.prototype, "previewAttachmentId", void 0);
1586
+ _decorate([state()], DocksCognitiveView.prototype, "toolDetail", void 0);
1354
1587
  _decorate([state()], DocksCognitiveView.prototype, "busy", void 0);
1588
+ _decorate([state()], DocksCognitiveView.prototype, "busyStatus", void 0);
1355
1589
  _decorate([state()], DocksCognitiveView.prototype, "pendingPermission", void 0);
1356
1590
  _decorate([state()], DocksCognitiveView.prototype, "policyMode", void 0);
1357
1591
  _decorate([state()], DocksCognitiveView.prototype, "optimizeFor", void 0);
@@ -1364,14 +1598,14 @@ _decorate([state()], DocksCognitiveView.prototype, "historyOpen", void 0);
1364
1598
  _decorate([state()], DocksCognitiveView.prototype, "historyQuery", void 0);
1365
1599
  DocksCognitiveView = _decorate([customElement("docks-cognitive-view")], DocksCognitiveView);
1366
1600
  var APPROVAL_OPTIONS$1 = [
1367
- {
1368
- value: "manual",
1369
- label: "Manual"
1370
- },
1371
1601
  {
1372
1602
  value: "auto_safe",
1373
1603
  label: "Auto-approve non-critical"
1374
1604
  },
1605
+ {
1606
+ value: "manual",
1607
+ label: "Manual"
1608
+ },
1375
1609
  {
1376
1610
  value: "auto_all",
1377
1611
  label: "Approve all"
@@ -1380,15 +1614,40 @@ var APPROVAL_OPTIONS$1 = [
1380
1614
  function capitalize(value) {
1381
1615
  return value ? value[0].toUpperCase() + value.slice(1) : value;
1382
1616
  }
1617
+ /**
1618
+ * Prefer text after the last tool/reasoning as the visible reply.
1619
+ * Interim narration stays out of the bubble — tool details cover the execution trail.
1620
+ */
1621
+ function splitAssistantPresentation(parts) {
1622
+ const techParts = parts.filter((p) => p.type !== "text");
1623
+ let lastTechIndex = -1;
1624
+ for (let i = 0; i < parts.length; i++) if (parts[i].type !== "text") lastTechIndex = i;
1625
+ if (lastTechIndex < 0) return {
1626
+ summaryText: parts.filter((p) => p.type === "text"),
1627
+ techParts
1628
+ };
1629
+ const afterTools = parts.slice(lastTechIndex + 1).filter((p) => p.type === "text").filter((p) => p.text.trim());
1630
+ if (afterTools.length) return {
1631
+ summaryText: afterTools,
1632
+ techParts
1633
+ };
1634
+ return {
1635
+ summaryText: [],
1636
+ techParts
1637
+ };
1638
+ }
1383
1639
  function technicalSummary(parts) {
1384
1640
  const tools = parts.filter((p) => p.type === "tool");
1385
- const reasoning = parts.some((p) => p.type === "reasoning");
1386
- const steps = parts.filter((p) => p.type === "step");
1387
- const bits = [];
1388
- if (tools.length) bits.push(tools.map((t) => `${t.name} · ${t.status}${t.durationMs != null ? ` · ${t.durationMs}ms` : ""}`).join(", "));
1389
- if (reasoning) bits.push("Thinking");
1390
- if (steps.length) bits.push(`${steps.length} step${steps.length === 1 ? "" : "s"}`);
1391
- return bits.join(" · ") || "Details";
1641
+ const done = tools.filter((t) => t.status === "done").length;
1642
+ const errors = tools.filter((t) => t.status === "error").length;
1643
+ if (!tools.length) {
1644
+ if (parts.some((p) => p.type === "reasoning")) return "Thinking…";
1645
+ return "Details";
1646
+ }
1647
+ if (errors && done) return `Finished with ${errors} issue${errors === 1 ? "" : "s"} (${done} ok).`;
1648
+ if (errors) return `Couldn't complete ${errors} step${errors === 1 ? "" : "s"}.`;
1649
+ if (tools.some((t) => t.status === "running" || t.status === "preparing")) return "Working…";
1650
+ return done === 1 ? "Done." : `Done · ${done} steps.`;
1392
1651
  }
1393
1652
  function startOfDay(ts) {
1394
1653
  const d = new Date(ts);
@@ -1463,6 +1722,29 @@ function shortModelLabel(modelRef) {
1463
1722
  const slash = modelRef.indexOf("/");
1464
1723
  return slash === -1 ? modelRef : modelRef.slice(slash + 1);
1465
1724
  }
1725
+ function filesFromClipboard(data) {
1726
+ if (!data) return [];
1727
+ const fromFiles = [...data.files ?? []].filter((f) => f && f.size > 0);
1728
+ if (fromFiles.length) return fromFiles;
1729
+ const fromItems = [];
1730
+ for (const item of data.items ?? []) {
1731
+ if (item.kind !== "file") continue;
1732
+ const file = item.getAsFile();
1733
+ if (!file || file.size <= 0) continue;
1734
+ fromItems.push(nameClipboardFile(file, item.type));
1735
+ }
1736
+ return fromItems;
1737
+ }
1738
+ function nameClipboardFile(file, mimeHint) {
1739
+ const mime = (file.type || mimeHint || "application/octet-stream").toLowerCase();
1740
+ if (file.name && file.name !== "image.png" && file.name !== "blob") return file;
1741
+ const ext = mime === "image/jpeg" || mime === "image/jpg" ? "jpg" : mime === "image/png" ? "png" : mime === "image/gif" ? "gif" : mime === "image/webp" ? "webp" : mime === "image/svg+xml" ? "svg" : mime.startsWith("image/") ? "png" : "bin";
1742
+ const name = `clipboard-${Date.now()}.${ext}`;
1743
+ return new File([file], name, {
1744
+ type: mime || file.type,
1745
+ lastModified: file.lastModified
1746
+ });
1747
+ }
1466
1748
  function mergePart(parts, part) {
1467
1749
  const next = [...parts];
1468
1750
  const last = next[next.length - 1];
@@ -1491,16 +1773,16 @@ function mergePart(parts, part) {
1491
1773
  //#endregion
1492
1774
  //#region src/view/cognitive-settings-editor.ts
1493
1775
  var APPROVAL_OPTIONS = [
1494
- {
1495
- value: "manual",
1496
- label: "Manual",
1497
- hint: "Confirm every tool before it runs"
1498
- },
1499
1776
  {
1500
1777
  value: "auto_safe",
1501
1778
  label: "Auto-approve non-critical",
1502
1779
  hint: "The agent classifies each tool as safe or critical; only critical ones ask for approval"
1503
1780
  },
1781
+ {
1782
+ value: "manual",
1783
+ label: "Manual",
1784
+ hint: "Confirm every tool before it runs"
1785
+ },
1504
1786
  {
1505
1787
  value: "auto_all",
1506
1788
  label: "Approve all",
@@ -1711,59 +1993,15 @@ var DocksCognitiveSettingsEditor = class DocksCognitiveSettingsEditor extends Do
1711
1993
  toastError(err instanceof Error ? err.message : String(err));
1712
1994
  }
1713
1995
  }
1714
- renderToolbar() {
1715
- return html`
1716
- <wa-button size="s" appearance="plain" title="Add provider" @click=${() => this.addProvider()}>
1717
- <wa-icon name="plus" label="Add provider"></wa-icon>
1718
- </wa-button>
1719
- <wa-button
1720
- size="s"
1721
- variant="brand"
1722
- ?disabled=${!this.isDirty()}
1723
- @click=${() => void this.save()}
1724
- >
1725
- <wa-icon slot="start" name="floppy-disk"></wa-icon>
1726
- Save
1727
- </wa-button>
1728
- `;
1729
- }
1730
1996
  renderContent() {
1731
1997
  const rows = this.settings.providers;
1732
1998
  return html`
1733
1999
  <div class="editor">
1734
- <section class="section">
1735
- <header class="header">
1736
- <div>
1737
- <h2>Tool approvals</h2>
1738
- <p class="hint">How the agent handles tool confirmation — same options as GitHub Copilot.</p>
1739
- </div>
1740
- </header>
1741
- <wa-radio-group
1742
- name="approval-mode"
1743
- label="Approval mode"
1744
- orientation="vertical"
1745
- .value=${this.approvalMode}
1746
- @change=${(e) => {
1747
- const value = e.target.value;
1748
- if (value === "manual" || value === "auto_safe" || value === "auto_all") this.setApprovalMode(value);
1749
- }}
1750
- >
1751
- ${APPROVAL_OPTIONS.map((option) => html`
1752
- <wa-radio value=${option.value}>
1753
- <span class="approval-option">
1754
- <span class="approval-label">${option.label}</span>
1755
- <span class="approval-hint">${option.hint}</span>
1756
- </span>
1757
- </wa-radio>
1758
- `)}
1759
- </wa-radio-group>
1760
- </section>
1761
-
1762
2000
  <section class="section">
1763
2001
  <header class="header">
1764
2002
  <div>
1765
2003
  <h2>AI Providers</h2>
1766
- <p class="hint">OpenAI-compatible endpoints. Edit cells in place, then Save.</p>
2004
+ <p class="hint">OpenAI-compatible endpoints. Edit cells in place, then save from the toolbar.</p>
1767
2005
  </div>
1768
2006
  <wa-button size="s" variant="brand" appearance="filled" @click=${() => this.addProvider()}>
1769
2007
  <wa-icon slot="start" name="plus"></wa-icon>
@@ -1798,6 +2036,34 @@ var DocksCognitiveSettingsEditor = class DocksCognitiveSettingsEditor extends Do
1798
2036
  <code>providerId/modelId</code> for Auto routing.
1799
2037
  </wa-callout>
1800
2038
  </section>
2039
+
2040
+ <section class="section">
2041
+ <header class="header">
2042
+ <div>
2043
+ <h2>Tool approvals</h2>
2044
+ <p class="hint">How the agent handles tool confirmation — same options as GitHub Copilot.</p>
2045
+ </div>
2046
+ </header>
2047
+ <wa-radio-group
2048
+ name="approval-mode"
2049
+ label="Approval mode"
2050
+ orientation="vertical"
2051
+ .value=${this.approvalMode}
2052
+ @change=${(e) => {
2053
+ const value = e.target.value;
2054
+ if (value === "manual" || value === "auto_safe" || value === "auto_all") this.setApprovalMode(value);
2055
+ }}
2056
+ >
2057
+ ${APPROVAL_OPTIONS.map((option) => html`
2058
+ <wa-radio value=${option.value}>
2059
+ <span class="approval-option">
2060
+ <span class="approval-label">${option.label}</span>
2061
+ <span class="approval-hint">${option.hint}</span>
2062
+ </span>
2063
+ </wa-radio>
2064
+ `)}
2065
+ </wa-radio-group>
2066
+ </section>
1801
2067
  </div>
1802
2068
  `;
1803
2069
  }
@@ -2126,4 +2392,4 @@ registerAll({
2126
2392
  });
2127
2393
  //#endregion
2128
2394
 
2129
- //# sourceMappingURL=cognitive-extension-CG_d39K5.js.map
2395
+ //# sourceMappingURL=cognitive-extension-7_ZMUCGD.js.map