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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/api.d.ts +1 -1
  2. package/dist/api.d.ts.map +1 -1
  3. package/dist/api.js +87 -2
  4. package/dist/api.js.map +1 -0
  5. package/dist/{cognitive-extension-7_ZMUCGD.js → cognitive-extension-BKGxwLYj.js} +169 -13
  6. package/dist/cognitive-extension-BKGxwLYj.js.map +1 -0
  7. package/dist/cognitive-extension.d.ts.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/runtime/cognitive-runtime.d.ts +5 -0
  10. package/dist/runtime/cognitive-runtime.d.ts.map +1 -1
  11. package/dist/runtime/context/usage.d.ts +3 -0
  12. package/dist/runtime/context/usage.d.ts.map +1 -1
  13. package/dist/runtime/index.d.ts +2 -0
  14. package/dist/runtime/index.d.ts.map +1 -1
  15. package/dist/runtime/models/openai-compatible.d.ts.map +1 -1
  16. package/dist/runtime/models/types.d.ts +6 -1
  17. package/dist/runtime/models/types.d.ts.map +1 -1
  18. package/dist/runtime/skills-commands.d.ts +3 -0
  19. package/dist/runtime/skills-commands.d.ts.map +1 -0
  20. package/dist/runtime/skills-parse.d.ts +14 -0
  21. package/dist/runtime/skills-parse.d.ts.map +1 -0
  22. package/dist/runtime/skills-resolve.d.ts +9 -0
  23. package/dist/runtime/skills-resolve.d.ts.map +1 -0
  24. package/dist/runtime/skills-select.d.ts +9 -0
  25. package/dist/runtime/skills-select.d.ts.map +1 -0
  26. package/dist/runtime/skills-types.d.ts +47 -0
  27. package/dist/runtime/skills-types.d.ts.map +1 -0
  28. package/dist/runtime/skills.d.ts +13 -0
  29. package/dist/runtime/skills.d.ts.map +1 -0
  30. package/dist/runtime/specialists/index.d.ts.map +1 -1
  31. package/dist/runtime/specialists/loop.d.ts.map +1 -1
  32. package/dist/runtime/tools/fetch-adapter.d.ts +4 -0
  33. package/dist/runtime/tools/fetch-adapter.d.ts.map +1 -0
  34. package/dist/runtime/tools/index.d.ts +1 -0
  35. package/dist/runtime/tools/index.d.ts.map +1 -1
  36. package/dist/runtime/types.d.ts +7 -0
  37. package/dist/runtime/types.d.ts.map +1 -1
  38. package/dist/{runtime-By5PP2BC.js → runtime-B0IHjzQO.js} +345 -16
  39. package/dist/runtime-B0IHjzQO.js.map +1 -0
  40. package/dist/view/cognitive-view.d.ts +1 -0
  41. package/dist/view/cognitive-view.d.ts.map +1 -1
  42. package/package.json +1 -1
  43. package/dist/cognitive-extension-7_ZMUCGD.js.map +0 -1
  44. package/dist/runtime-By5PP2BC.js.map +0 -1
package/dist/api.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { cognitiveRuntime, CognitiveRuntime, TOPIC, AI_VIEW_NAME, AI_SETTINGS_KEY, AI_SETTINGS_EDITOR_ID, OPEN_AI_SETTINGS_COMMAND, defaultProvidersSettings, fetchOpenAICompatibleModels, listConfiguredModelIds, loadModelsCatalog, parseModelIds, OpenAICompatibleProvider, type ChatMessage, type MessagePart, type PromptAttachment, type SessionAttachment, type CognitiveSession, type Checkpoint, type ModelPolicy, type EffortLevel, type OptimizeFor, type SpecialistRole, type PermissionAction, type ToolApprovalMode, type EmotionalState, type ProviderConfig, type ProvidersSettings, type RemoteModelInfo, type ModelProvider, type StreamParams, type StreamChunk, type ProviderChatMessage, type SystemPromptEnhancer, } from './runtime';
1
+ export { cognitiveRuntime, CognitiveRuntime, TOPIC, AI_VIEW_NAME, AI_SETTINGS_KEY, AI_SETTINGS_EDITOR_ID, OPEN_AI_SETTINGS_COMMAND, defaultProvidersSettings, fetchOpenAICompatibleModels, listConfiguredModelIds, loadModelsCatalog, parseModelIds, OpenAICompatibleProvider, type ChatMessage, type MessagePart, type PromptAttachment, type SessionAttachment, type CognitiveSession, type Checkpoint, type ModelPolicy, type EffortLevel, type OptimizeFor, type SpecialistRole, type PermissionAction, type ToolApprovalMode, type TokenUsage, type EmotionalState, type ProviderConfig, type ProvidersSettings, type RemoteModelInfo, type ModelProvider, type StreamParams, type StreamChunk, type ProviderChatMessage, type SystemPromptEnhancer, type AgentSkill, type ResolvedSkill, type SkillContribution, type SkillLoadResult, type SkillRelevanceContext, SYSTEM_SKILLS, parseSkillMarkdown, formatSkillForPrompt, formatSkillsPromptSection, registerSkillMarkdown, registerSkillCommands, listSkillContributions, resolveSkill, resolveSkillBody, selectRelevantSkills, isSkillContribution, } from './runtime';
2
2
  //# sourceMappingURL=api.d.ts.map
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,EACL,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,wBAAwB,EACxB,wBAAwB,EACxB,2BAA2B,EAC3B,sBAAsB,EACtB,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,GAC1B,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,KAAK,EACL,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,wBAAwB,EACxB,wBAAwB,EACxB,2BAA2B,EAC3B,sBAAsB,EACtB,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,WAAW,CAAC"}
package/dist/api.js CHANGED
@@ -1,2 +1,87 @@
1
- import { _ as AI_SETTINGS_KEY, b as TOPIC, d as defaultProvidersSettings, f as listConfiguredModelIds, g as AI_SETTINGS_EDITOR_ID, h as loadModelsCatalog, l as OpenAICompatibleProvider, n as cognitiveRuntime, p as parseModelIds, t as CognitiveRuntime, u as fetchOpenAICompatibleModels, v as AI_VIEW_NAME, y as OPEN_AI_SETTINGS_COMMAND } from "./runtime-By5PP2BC.js";
2
- export { AI_SETTINGS_EDITOR_ID, AI_SETTINGS_KEY, AI_VIEW_NAME, CognitiveRuntime, OPEN_AI_SETTINGS_COMMAND, OpenAICompatibleProvider, TOPIC, cognitiveRuntime, defaultProvidersSettings, fetchOpenAICompatibleModels, listConfiguredModelIds, loadModelsCatalog, parseModelIds };
1
+ import { D as AI_VIEW_NAME, E as AI_SETTINGS_KEY, O as OPEN_AI_SETTINGS_COMMAND, S as parseModelIds, T as AI_SETTINGS_EDITOR_ID, a as listSkillContributions, b as defaultProvidersSettings, c as resolveSkillBody, d as parseSkillMarkdown, i as SYSTEM_SKILLS, k as TOPIC, l as isSkillContribution, n as CognitiveRuntime, o as registerSkillMarkdown, r as cognitiveRuntime, s as resolveSkill, t as registerSkillCommands, u as formatSkillForPrompt, v as OpenAICompatibleProvider, w as loadModelsCatalog, x as listConfiguredModelIds, y as fetchOpenAICompatibleModels } from "./runtime-B0IHjzQO.js";
2
+ //#region src/runtime/skills-select.ts
3
+ /**
4
+ * Catalog-only prompt section. The model loads full skill bodies via skills.load.
5
+ */
6
+ function formatSkillsPromptSection(skills) {
7
+ if (!skills.length) return "";
8
+ return ["Available skills (call skills.load with the skill name to load full instructions before relying on them):", skills.map((skill) => `- ${skill.name}: ${(skill.description ?? "").trim() || skill.label}`).join("\n")].join("\n");
9
+ }
10
+ /** @deprecated Prefer skills.load; kept for tests/helpers that still filter by goal. */
11
+ function selectRelevantSkills(skills, ctx) {
12
+ return skills.filter((skill) => {
13
+ if (skill.relevant) return skill.relevant(ctx);
14
+ return skillMatchesGoal(skill, ctx.goal);
15
+ });
16
+ }
17
+ var STOP_WORDS = /* @__PURE__ */ new Set([
18
+ "that",
19
+ "this",
20
+ "with",
21
+ "from",
22
+ "your",
23
+ "have",
24
+ "when",
25
+ "into",
26
+ "call",
27
+ "file",
28
+ "files",
29
+ "default",
30
+ "export",
31
+ "write",
32
+ "using",
33
+ "such",
34
+ "does",
35
+ "send",
36
+ "open",
37
+ "name",
38
+ "they",
39
+ "them",
40
+ "then",
41
+ "than",
42
+ "also",
43
+ "only",
44
+ "over",
45
+ "under",
46
+ "about",
47
+ "after",
48
+ "before",
49
+ "other",
50
+ "which",
51
+ "where",
52
+ "whose",
53
+ "their",
54
+ "there",
55
+ "these",
56
+ "those",
57
+ "and",
58
+ "for",
59
+ "the",
60
+ "a",
61
+ "an",
62
+ "to",
63
+ "of",
64
+ "in",
65
+ "on",
66
+ "or",
67
+ "by",
68
+ "is",
69
+ "are",
70
+ "be",
71
+ "as",
72
+ "at",
73
+ "it",
74
+ "do",
75
+ "not"
76
+ ]);
77
+ function skillMatchesGoal(skill, goal) {
78
+ const hay = goal.toLowerCase();
79
+ if (!hay.trim()) return false;
80
+ const tokens = `${skill.name} ${skill.label ?? ""} ${skill.description ?? ""}`.toLowerCase().split(/[^a-z0-9._-]+/).filter((token) => token.length >= 4 && !STOP_WORDS.has(token));
81
+ if (!tokens.length) return false;
82
+ return tokens.some((token) => hay.includes(token));
83
+ }
84
+ //#endregion
85
+ export { AI_SETTINGS_EDITOR_ID, AI_SETTINGS_KEY, AI_VIEW_NAME, CognitiveRuntime, OPEN_AI_SETTINGS_COMMAND, OpenAICompatibleProvider, SYSTEM_SKILLS, TOPIC, cognitiveRuntime, defaultProvidersSettings, fetchOpenAICompatibleModels, formatSkillForPrompt, formatSkillsPromptSection, isSkillContribution, listConfiguredModelIds, listSkillContributions, loadModelsCatalog, parseModelIds, parseSkillMarkdown, registerSkillCommands, registerSkillMarkdown, resolveSkill, resolveSkillBody, selectRelevantSkills };
86
+
87
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","names":[],"sources":["../src/runtime/skills-select.ts"],"sourcesContent":["import type { SkillContribution, SkillRelevanceContext } from './skills-types';\n\nexport type { SkillContribution, SkillRelevanceContext } from './skills-types';\n\n/**\n * Catalog-only prompt section. The model loads full skill bodies via skills.load.\n */\nexport function formatSkillsPromptSection(skills: SkillContribution[]): string {\n if (!skills.length) return '';\n const catalog = skills\n .map((skill) => `- ${skill.name}: ${(skill.description ?? '').trim() || skill.label}`)\n .join('\\n');\n return [\n 'Available skills (call skills.load with the skill name to load full instructions before relying on them):',\n catalog,\n ].join('\\n');\n}\n\n/** @deprecated Prefer skills.load; kept for tests/helpers that still filter by goal. */\nexport function selectRelevantSkills(\n skills: SkillContribution[],\n ctx: SkillRelevanceContext,\n): SkillContribution[] {\n return skills.filter((skill) => {\n if (skill.relevant) return skill.relevant(ctx);\n return skillMatchesGoal(skill, ctx.goal);\n });\n}\n\nconst STOP_WORDS = new Set([\n 'that',\n 'this',\n 'with',\n 'from',\n 'your',\n 'have',\n 'when',\n 'into',\n 'call',\n 'file',\n 'files',\n 'default',\n 'export',\n 'write',\n 'using',\n 'such',\n 'does',\n 'send',\n 'open',\n 'name',\n 'they',\n 'them',\n 'then',\n 'than',\n 'also',\n 'only',\n 'over',\n 'under',\n 'about',\n 'after',\n 'before',\n 'other',\n 'which',\n 'where',\n 'whose',\n 'their',\n 'there',\n 'these',\n 'those',\n 'and',\n 'for',\n 'the',\n 'a',\n 'an',\n 'to',\n 'of',\n 'in',\n 'on',\n 'or',\n 'by',\n 'is',\n 'are',\n 'be',\n 'as',\n 'at',\n 'it',\n 'do',\n 'not',\n]);\n\nfunction skillMatchesGoal(skill: SkillContribution, goal: string): boolean {\n const hay = goal.toLowerCase();\n if (!hay.trim()) return false;\n const tokens = `${skill.name} ${skill.label ?? ''} ${skill.description ?? ''}`\n .toLowerCase()\n .split(/[^a-z0-9._-]+/)\n .filter((token) => token.length >= 4 && !STOP_WORDS.has(token));\n if (!tokens.length) return false;\n return tokens.some((token) => hay.includes(token));\n}\n"],"mappings":";;;;;AAOA,SAAgB,0BAA0B,QAAqC;CAC7E,IAAI,CAAC,OAAO,QAAQ,OAAO;CAI3B,OAAO,CACL,6GAJc,OACb,KAAK,UAAU,KAAK,MAAM,KAAK,KAAK,MAAM,eAAe,GAAA,CAAI,KAAK,KAAK,MAAM,OAAO,CAAC,CACrF,KAAK,IAGN,CACF,CAAC,CAAC,KAAK,IAAI;AACb;;AAGA,SAAgB,qBACd,QACA,KACqB;CACrB,OAAO,OAAO,QAAQ,UAAU;EAC9B,IAAI,MAAM,UAAU,OAAO,MAAM,SAAS,GAAG;EAC7C,OAAO,iBAAiB,OAAO,IAAI,IAAI;CACzC,CAAC;AACH;AAEA,IAAM,6BAAa,IAAI,IAAI;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,iBAAiB,OAA0B,MAAuB;CACzE,MAAM,MAAM,KAAK,YAAY;CAC7B,IAAI,CAAC,IAAI,KAAK,GAAG,OAAO;CACxB,MAAM,SAAS,GAAG,MAAM,KAAK,GAAG,MAAM,SAAS,GAAG,GAAG,MAAM,eAAe,KACvE,YAAY,CAAC,CACb,MAAM,eAAe,CAAC,CACtB,QAAQ,UAAU,MAAM,UAAU,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC;CAChE,IAAI,CAAC,OAAO,QAAQ,OAAO;CAC3B,OAAO,OAAO,MAAM,UAAU,IAAI,SAAS,KAAK,CAAC;AACnD"}
@@ -1,6 +1,6 @@
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
- import { DocksPart, SIDEBAR_AUXILIARY, confirmDialog, contributionRegistry, editorRegistry, registerAll, rootContext, toastError, toastInfo } from "@eclipse-docks/core";
3
- import { css, customElement, html, nothing, property, query, state } from "@eclipse-docks/core/externals/lit";
1
+ import { C as detectEffortCapability, D as AI_VIEW_NAME, E as AI_SETTINGS_KEY, O as OPEN_AI_SETTINGS_COMMAND, S as parseModelIds, T as AI_SETTINGS_EDITOR_ID, _ as readPromptAttachmentFromFile, b as defaultProvidersSettings, f as estimateSessionContextUsage, g as ATTACHMENT_FILE_ACCEPT, h as formatTokenCount, k as TOPIC, m as formatContextPercent, p as formatBucketDetail, r as cognitiveRuntime, t as registerSkillCommands, y as fetchOpenAICompatibleModels } from "./runtime-B0IHjzQO.js";
2
+ import { DocksPart, File as File$1, FileContentType, SIDEBAR_AUXILIARY, confirmDialog, contributionRegistry, editorRegistry, filebrowserDialog, parseMarkdownHtml, registerAll, rootContext, toastError, toastInfo, workspaceService } from "@eclipse-docks/core";
3
+ import { css, customElement, html, nothing, property, query, state, unsafeHTML } 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
6
  //#region src/view/busy-status.ts
@@ -180,11 +180,21 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
180
180
  display: flex;
181
181
  flex-direction: column;
182
182
  gap: var(--wa-space-2xs);
183
- max-width: 100%;
183
+ width: 90%;
184
+ min-width: 90%;
185
+ max-width: 90%;
186
+ align-self: flex-start;
187
+ align-items: stretch;
184
188
  }
185
189
 
186
190
  .msg.user {
187
- align-items: flex-end;
191
+ align-self: flex-end;
192
+ }
193
+
194
+ .msg .bubble,
195
+ .msg wa-details.msg-details {
196
+ width: 100%;
197
+ box-sizing: border-box;
188
198
  }
189
199
 
190
200
  .msg-meta {
@@ -222,6 +232,39 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
222
232
  white-space: pre-wrap;
223
233
  line-height: var(--wa-line-height-normal);
224
234
  max-width: 100%;
235
+ overflow-wrap: anywhere;
236
+ }
237
+
238
+ .bubble.markdown {
239
+ white-space: normal;
240
+ }
241
+
242
+ .bubble.markdown > :first-child {
243
+ margin-block-start: 0;
244
+ }
245
+
246
+ .bubble.markdown > :last-child {
247
+ margin-block-end: 0;
248
+ }
249
+
250
+ .bubble.markdown pre {
251
+ overflow-x: auto;
252
+ padding: var(--wa-space-s);
253
+ border-radius: var(--wa-border-radius-m);
254
+ background: color-mix(in srgb, var(--wa-color-neutral-fill-quiet) 55%, transparent);
255
+ }
256
+
257
+ .bubble.markdown code {
258
+ font-family: var(--wa-font-family-code, ui-monospace, monospace);
259
+ font-size: 0.92em;
260
+ }
261
+
262
+ .bubble.markdown pre code {
263
+ font-size: inherit;
264
+ }
265
+
266
+ .bubble.markdown a {
267
+ color: var(--wa-color-brand-on-normal);
225
268
  }
226
269
 
227
270
  .msg.user .bubble {
@@ -318,6 +361,34 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
318
361
  background: var(--bucket-color, var(--wa-color-brand-fill-normal));
319
362
  }
320
363
 
364
+ .context-io {
365
+ display: grid;
366
+ grid-template-columns: 1fr 1fr;
367
+ gap: var(--wa-space-2xs);
368
+ padding-top: var(--wa-space-s);
369
+ border-top: var(--wa-border-width-s) solid var(--wa-color-border-normal);
370
+ font-size: var(--wa-font-size-xs);
371
+ }
372
+
373
+ .context-io-heading {
374
+ grid-column: 1 / -1;
375
+ font-weight: var(--wa-font-weight-semibold, 600);
376
+ }
377
+
378
+ .context-io-item {
379
+ display: flex;
380
+ flex-direction: column;
381
+ gap: 0.1rem;
382
+ }
383
+
384
+ .context-io-item span {
385
+ color: var(--wa-color-text-quiet);
386
+ }
387
+
388
+ .context-io-item strong {
389
+ font-variant-numeric: tabular-nums;
390
+ }
391
+
321
392
  .context-footer {
322
393
  display: flex;
323
394
  flex-direction: column;
@@ -823,6 +894,40 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
823
894
  if (this.previewAttachmentId === attachmentId) this.previewAttachmentId = null;
824
895
  this.syncSessions();
825
896
  }
897
+ async saveAttachmentToWorkspace(attachmentId) {
898
+ const attachment = this.sessionAttachments().find((a) => a.id === attachmentId);
899
+ if (!attachment) return;
900
+ const preferredName = attachmentSaveFileName(attachment);
901
+ const parent = await filebrowserDialog("directory");
902
+ if (!parent) return;
903
+ try {
904
+ const workspace = await workspaceService.getWorkspace();
905
+ if (!workspace) {
906
+ toastError("No workspace connected");
907
+ return;
908
+ }
909
+ const parentPath = parent.replace(/\/$/, "");
910
+ const fileName = await uniqueWorkspaceFileName(workspace, parentPath, preferredName);
911
+ const path = `${parentPath}/${fileName}`;
912
+ const resource = await workspace.getResource(path, { create: true });
913
+ if (!(resource instanceof File$1)) {
914
+ toastError("Could not create file in the selected folder");
915
+ return;
916
+ }
917
+ if (attachment.dataUrl) {
918
+ const blob = await (await fetch(attachment.dataUrl)).blob();
919
+ const named = new globalThis.File([blob], fileName, { type: blob.type || attachment.mime || "" });
920
+ await resource.saveContents(named, { contentType: FileContentType.BINARY });
921
+ } else if (attachment.text != null) await resource.saveContents(attachment.text);
922
+ else {
923
+ toastError("Attachment has no payload to save");
924
+ return;
925
+ }
926
+ toastInfo(`Saved ${fileName}`);
927
+ } catch (err) {
928
+ toastError(err instanceof Error ? err.message : String(err));
929
+ }
930
+ }
826
931
  async addFiles(fileList) {
827
932
  const files = [...fileList];
828
933
  if (!files.length) return;
@@ -955,6 +1060,15 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
955
1060
  `}
956
1061
  <span class="attach-name">${file.name}</span>
957
1062
  </button>
1063
+ <wa-button
1064
+ size="s"
1065
+ appearance="plain"
1066
+ title=${`Save ${file.name} to workspace folder`}
1067
+ ?disabled=${this.busy}
1068
+ @click=${() => void this.saveAttachmentToWorkspace(file.id)}
1069
+ >
1070
+ <wa-icon name="floppy-disk" label=${`Save ${file.name}`}></wa-icon>
1071
+ </wa-button>
958
1072
  <wa-button
959
1073
  size="s"
960
1074
  appearance="plain"
@@ -1079,7 +1193,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1079
1193
  const usage = this.contextUsage();
1080
1194
  const canCompact = (this.focusedSession()?.history.length ?? 0) > 12;
1081
1195
  const percentLabel = formatContextPercent(usage.percent);
1082
- const title = `Context ${percentLabel} · ${formatTokenCount(usage.used)} / ${formatTokenCount(usage.limit)} tokens`;
1196
+ const title = `Context window ${percentLabel} · ~${formatTokenCount(usage.used)} / ${formatTokenCount(usage.limit)}`;
1083
1197
  const ringValue = Math.min(100, Math.max(usage.percent, usage.used > 0 ? 1 : 0));
1084
1198
  return html`
1085
1199
  <wa-dropdown
@@ -1108,7 +1222,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1108
1222
  @keydown=${(e) => e.stopPropagation()}
1109
1223
  >
1110
1224
  <div class="context-header">
1111
- <strong>Context tokens</strong>
1225
+ <strong>Context window</strong>
1112
1226
  <span>${percentLabel}</span>
1113
1227
  </div>
1114
1228
  <div class="context-bars">
@@ -1129,10 +1243,26 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1129
1243
  </div>
1130
1244
  <div class="context-footer">
1131
1245
  <span class="context-remaining">
1132
- ~${formatTokenCount(usage.used)} used · ${formatTokenCount(usage.remaining)} free of
1246
+ ~${formatTokenCount(usage.used)} in window · ${formatTokenCount(usage.remaining)} free of
1133
1247
  ${formatTokenCount(usage.limit)}
1134
1248
  · ${shortModelLabel(usage.modelRef)}
1135
1249
  </span>
1250
+ </div>
1251
+ <div
1252
+ class="context-io"
1253
+ title="Sum of provider-reported tokens across every model request in this chat. Each tool-loop call re-sends the prompt, so API input grows much faster than the context window fill."
1254
+ >
1255
+ <div class="context-io-heading">API usage (this chat)</div>
1256
+ <div class="context-io-item">
1257
+ <span>Input</span>
1258
+ <strong>${usage.inputTokens ? formatTokenCount(usage.inputTokens) : "—"}</strong>
1259
+ </div>
1260
+ <div class="context-io-item">
1261
+ <span>Output</span>
1262
+ <strong>${usage.outputTokens ? formatTokenCount(usage.outputTokens) : "—"}</strong>
1263
+ </div>
1264
+ </div>
1265
+ <div class="context-footer">
1136
1266
  <wa-button
1137
1267
  size="s"
1138
1268
  variant=${usage.level === "critical" ? "danger" : "neutral"}
@@ -1495,7 +1625,7 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1495
1625
  renderAssistantMessage(message, opts = {}) {
1496
1626
  const { summaryText, techParts } = splitAssistantPresentation(message.parts);
1497
1627
  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>`;
1628
+ const summaryBubble = summaryText.length ? summaryText.map((p) => this.renderTextPart(p, { markdown: true })) : opts.streaming ? nothing : html`<div class="bubble">${technicalSummary(techParts)}</div>`;
1499
1629
  const summary = html`
1500
1630
  <div slot="summary" class="msg-summary">
1501
1631
  ${opts.streaming ? html`
@@ -1541,9 +1671,10 @@ var DocksCognitiveView = class DocksCognitiveView extends DocksPart {
1541
1671
  </div>
1542
1672
  `;
1543
1673
  }
1544
- renderTextPart(part) {
1545
- if (part.type === "text") return html`<div class="bubble">${part.text}</div>`;
1546
- return nothing;
1674
+ renderTextPart(part, opts = {}) {
1675
+ if (part.type !== "text") return nothing;
1676
+ if (opts.markdown) return html`<div class="bubble markdown">${unsafeHTML(parseMarkdownHtml(part.text))}</div>`;
1677
+ return html`<div class="bubble">${part.text}</div>`;
1547
1678
  }
1548
1679
  renderTechnicalPart(part) {
1549
1680
  if (part.type === "reasoning") return html`
@@ -1614,6 +1745,30 @@ var APPROVAL_OPTIONS$1 = [
1614
1745
  function capitalize(value) {
1615
1746
  return value ? value[0].toUpperCase() + value.slice(1) : value;
1616
1747
  }
1748
+ /** Basename used when saving an attachment into a workspace folder. */
1749
+ function attachmentSaveFileName(attachment) {
1750
+ const fromName = attachment.name.trim().split(/[/\\]/).pop()?.trim();
1751
+ if (fromName) return fromName;
1752
+ const fromPath = attachment.path?.split(/[/\\]/).pop()?.trim();
1753
+ if (fromPath) return fromPath;
1754
+ if (attachment.kind === "image") return attachment.mime === "image/jpeg" ? "attachment.jpg" : "attachment.png";
1755
+ return "attachment.txt";
1756
+ }
1757
+ /** `photo.png` → `photo (1).png`, `photo (2).png`, … when the path is taken. */
1758
+ function withNumericSuffix(fileName, n) {
1759
+ const dot = fileName.lastIndexOf(".");
1760
+ if (dot > 0) return `${fileName.slice(0, dot)} (${n})${fileName.slice(dot)}`;
1761
+ return `${fileName} (${n})`;
1762
+ }
1763
+ async function uniqueWorkspaceFileName(workspace, parentPath, fileName) {
1764
+ const candidatePath = (name) => `${parentPath}/${name}`;
1765
+ if (!await workspace.getResource(candidatePath(fileName))) return fileName;
1766
+ for (let n = 1; n < 1e4; n++) {
1767
+ const next = withNumericSuffix(fileName, n);
1768
+ if (!await workspace.getResource(candidatePath(next))) return next;
1769
+ }
1770
+ return withNumericSuffix(fileName, Date.now());
1771
+ }
1617
1772
  /**
1618
1773
  * Prefer text after the last tool/reasoning as the visible reply.
1619
1774
  * Interim narration stays out of the bubble — tool details cover the execution trail.
@@ -2354,6 +2509,7 @@ function uniqueProviderId(existing) {
2354
2509
  //#endregion
2355
2510
  //#region src/cognitive-extension.ts
2356
2511
  cognitiveRuntime.start();
2512
+ registerSkillCommands();
2357
2513
  rootContext.put("cognitiveRuntime", cognitiveRuntime);
2358
2514
  contributionRegistry.registerContribution(SIDEBAR_AUXILIARY, {
2359
2515
  name: AI_VIEW_NAME,
@@ -2392,4 +2548,4 @@ registerAll({
2392
2548
  });
2393
2549
  //#endregion
2394
2550
 
2395
- //# sourceMappingURL=cognitive-extension-7_ZMUCGD.js.map
2551
+ //# sourceMappingURL=cognitive-extension-BKGxwLYj.js.map