@abot-ai/runtime 1.1.0 → 1.2.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.
Files changed (268) hide show
  1. package/README.md +6 -3
  2. package/dist/src/capabilities/tool-types.d.ts +1 -1
  3. package/dist/src/model-gateway/providers/ollama/generation-options.d.ts +1 -3
  4. package/dist/src/model-gateway/providers/ollama/generation-options.js +1 -5
  5. package/dist/src/model-gateway/providers/ollama/payload.js +0 -8
  6. package/dist/src/model-gateway/server/request-body.js +1 -0
  7. package/dist/src/plugin-sdk/index.d.ts +2 -0
  8. package/dist/src/plugin-sdk/index.js +2 -0
  9. package/dist/src/plugin-sdk/tool-availability-brief.d.ts +9 -0
  10. package/dist/src/plugin-sdk/tool-availability-brief.js +48 -0
  11. package/dist/src/plugin-sdk/tool-availability-overview.d.ts +9 -0
  12. package/dist/src/plugin-sdk/tool-availability-overview.js +62 -0
  13. package/dist/src/runtime/adapters/registered-tool-payload-plan.d.ts +0 -9
  14. package/dist/src/runtime/adapters/registered-tool-payload-plan.js +0 -67
  15. package/dist/src/runtime/adapters/registered-tool-payload-related-artifacts.d.ts +13 -0
  16. package/dist/src/runtime/adapters/registered-tool-payload-related-artifacts.js +84 -0
  17. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/catalog.js +4 -0
  18. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/execution.js +3 -0
  19. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/payload-lifecycle.d.ts +1 -0
  20. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/payload-lifecycle.js +11 -1
  21. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/payload-stages.d.ts +1 -0
  22. package/dist/src/runtime/adapters/registered-tool-worker-capabilities/payload-stages.js +9 -1
  23. package/dist/src/runtime/capabilities/request-bound-tool-registry.js +1 -24
  24. package/dist/src/runtime/capabilities/tool-availability.d.ts +6 -0
  25. package/dist/src/runtime/capabilities/tool-availability.js +28 -0
  26. package/dist/src/runtime/context/capability-brief.d.ts +25 -0
  27. package/dist/src/runtime/context/capability-brief.js +106 -0
  28. package/dist/src/runtime/context/request-tool-results-message-binding.d.ts +6 -0
  29. package/dist/src/runtime/context/request-tool-results-message-binding.js +30 -0
  30. package/dist/src/runtime/context/request-tool-results-scope.d.ts +3 -0
  31. package/dist/src/runtime/context/request-tool-results-scope.js +8 -0
  32. package/dist/src/runtime/context/request-tool-results.d.ts +8 -4
  33. package/dist/src/runtime/context/request-tool-results.js +49 -38
  34. package/dist/src/runtime/orchestration/role-calls/candidate-validation.js +8 -1
  35. package/dist/src/runtime/orchestration/role-calls/child-return.d.ts +4 -0
  36. package/dist/src/runtime/orchestration/role-calls/child-return.js +25 -5
  37. package/dist/src/runtime/orchestration/role-calls/command-decoder.js +8 -3
  38. package/dist/src/runtime/orchestration/role-calls/command-dispatch/child-transitions.d.ts +1 -1
  39. package/dist/src/runtime/orchestration/role-calls/command-dispatch/child-transitions.js +17 -2
  40. package/dist/src/runtime/orchestration/role-calls/command-dispatch.d.ts +1 -1
  41. package/dist/src/runtime/orchestration/role-calls/command-dispatch.js +2 -2
  42. package/dist/src/runtime/orchestration/role-calls/contracts.d.ts +5 -2
  43. package/dist/src/runtime/orchestration/role-calls/contracts.js +4 -4
  44. package/dist/src/runtime/orchestration/role-calls/dependency-results.d.ts +1 -1
  45. package/dist/src/runtime/orchestration/role-calls/dependency-results.js +1 -0
  46. package/dist/src/runtime/orchestration/role-calls/index.d.ts +5 -1
  47. package/dist/src/runtime/orchestration/role-calls/index.js +5 -1
  48. package/dist/src/runtime/orchestration/role-calls/ledger.d.ts +3 -1
  49. package/dist/src/runtime/orchestration/role-calls/ledger.js +9 -2
  50. package/dist/src/runtime/orchestration/role-calls/reducer.d.ts +1 -1
  51. package/dist/src/runtime/orchestration/role-calls/reducer.js +2 -2
  52. package/dist/src/runtime/orchestration/role-calls/result-receipt.d.ts +49 -0
  53. package/dist/src/runtime/orchestration/role-calls/result-receipt.js +188 -0
  54. package/dist/src/runtime/orchestration/role-calls/return-result-receipt.d.ts +19 -0
  55. package/dist/src/runtime/orchestration/role-calls/return-result-receipt.js +52 -0
  56. package/dist/src/runtime/orchestration/role-calls/transactions.js +1 -0
  57. package/dist/src/runtime/orchestration/role-calls/work-result-lineage.d.ts +34 -0
  58. package/dist/src/runtime/orchestration/role-calls/work-result-lineage.js +185 -0
  59. package/dist/src/runtime/orchestration/role-calls/work-result-receipt.d.ts +36 -0
  60. package/dist/src/runtime/orchestration/role-calls/work-result-receipt.js +187 -0
  61. package/dist/src/runtime/orchestration/role-executors/activation/result-normalization.js +6 -3
  62. package/dist/src/runtime/orchestration/role-executors/child-invocation/transaction.js +1 -0
  63. package/dist/src/runtime/orchestration/role-executors/contracts.d.ts +2 -1
  64. package/dist/src/runtime/orchestration/worker-capabilities/assignment-provenance.d.ts +36 -0
  65. package/dist/src/runtime/orchestration/worker-capabilities/assignment-provenance.js +171 -0
  66. package/dist/src/runtime/orchestration/worker-capabilities/contracts.d.ts +32 -1
  67. package/dist/src/runtime/orchestration/worker-capabilities/execution/invocation-preparation.d.ts +2 -1
  68. package/dist/src/runtime/orchestration/worker-capabilities/execution/invocation-preparation.js +3 -0
  69. package/dist/src/runtime/orchestration/worker-capabilities/execution.js +42 -19
  70. package/dist/src/runtime/orchestration/worker-capabilities/index.d.ts +2 -0
  71. package/dist/src/runtime/orchestration/worker-capabilities/index.js +2 -0
  72. package/dist/src/runtime/orchestration/worker-capabilities/payload-assignment-validation.d.ts +3 -0
  73. package/dist/src/runtime/orchestration/worker-capabilities/payload-assignment-validation.js +30 -0
  74. package/dist/src/runtime/orchestration/worker-capabilities/payload-author.js +8 -19
  75. package/dist/src/runtime/orchestration/worker-capabilities/payload-contracts.d.ts +5 -1
  76. package/dist/src/runtime/orchestration/worker-capabilities/payload-instructions.js +1 -1
  77. package/dist/src/runtime/orchestration/worker-capabilities/payload-source-provenance.d.ts +14 -0
  78. package/dist/src/runtime/orchestration/worker-capabilities/payload-source-provenance.js +132 -0
  79. package/dist/src/runtime/request/capability-brief.d.ts +5 -0
  80. package/dist/src/runtime/request/capability-brief.js +24 -0
  81. package/dist/src/runtime/request/execution-scope.d.ts +2 -1
  82. package/dist/src/runtime/request/role-executor-composition.js +1 -1
  83. package/dist/src/runtime/request/worker-capability-composition.d.ts +2 -1
  84. package/dist/src/runtime/request/worker-capability-composition.js +6 -1
  85. package/dist/src/runtime/request/worker-capability-payload.js +11 -8
  86. package/dist/src/runtime/steps/execution-agent/prompt.js +4 -3
  87. package/dist/src/runtime/steps/planner-decision/capability-catalog-context.d.ts +14 -0
  88. package/dist/src/runtime/steps/planner-decision/capability-catalog-context.js +87 -0
  89. package/dist/src/runtime/steps/planner-decision/contracts.d.ts +3 -5
  90. package/dist/src/runtime/steps/planner-decision/contracts.js +3 -3
  91. package/dist/src/runtime/steps/planner-decision/format.d.ts +3 -2
  92. package/dist/src/runtime/steps/planner-decision/format.js +73 -76
  93. package/dist/src/runtime/steps/planner-decision/input.d.ts +4 -3
  94. package/dist/src/runtime/steps/planner-decision/input.js +10 -7
  95. package/dist/src/runtime/steps/planner-decision/parser.d.ts +3 -2
  96. package/dist/src/runtime/steps/planner-decision/parser.js +10 -19
  97. package/dist/src/runtime/steps/planner-decision/plan-parser.js +18 -12
  98. package/dist/src/runtime/steps/planner-decision/prompt.js +33 -28
  99. package/dist/src/runtime/steps/planner-decision/resume.d.ts +1 -1
  100. package/dist/src/runtime/steps/planner-decision/resume.js +38 -1
  101. package/dist/src/runtime/steps/planner-decision/run.d.ts +3 -2
  102. package/dist/src/runtime/steps/reviewer-decision/audit-coverage.d.ts +10 -0
  103. package/dist/src/runtime/steps/reviewer-decision/audit-coverage.js +186 -0
  104. package/dist/src/runtime/steps/reviewer-decision/contracts.d.ts +3 -8
  105. package/dist/src/runtime/steps/reviewer-decision/contracts.js +12 -6
  106. package/dist/src/runtime/steps/reviewer-decision/delegated-context.d.ts +24 -0
  107. package/dist/src/runtime/steps/reviewer-decision/delegated-context.js +70 -0
  108. package/dist/src/runtime/steps/reviewer-decision/diagnostics.js +1 -1
  109. package/dist/src/runtime/steps/reviewer-decision/format.js +37 -24
  110. package/dist/src/runtime/steps/reviewer-decision/input.js +1 -1
  111. package/dist/src/runtime/steps/reviewer-decision/model-context.d.ts +4 -3
  112. package/dist/src/runtime/steps/reviewer-decision/model-context.js +92 -10
  113. package/dist/src/runtime/steps/reviewer-decision/parser.js +19 -1
  114. package/dist/src/runtime/steps/reviewer-decision/projection.js +42 -68
  115. package/dist/src/runtime/steps/reviewer-decision/prompt.js +10 -5
  116. package/dist/src/runtime/steps/reviewer-decision/run.js +12 -3
  117. package/dist/src/runtime/steps/reviewer-decision/verification-coverage.d.ts +14 -0
  118. package/dist/src/runtime/steps/reviewer-decision/verification-coverage.js +52 -0
  119. package/dist/src/runtime/steps/supervisor-decision/capability-brief.d.ts +14 -0
  120. package/dist/src/runtime/steps/supervisor-decision/capability-brief.js +38 -0
  121. package/dist/src/runtime/steps/supervisor-decision/contracts.d.ts +7 -3
  122. package/dist/src/runtime/steps/supervisor-decision/diagnostics.d.ts +2 -0
  123. package/dist/src/runtime/steps/supervisor-decision/diagnostics.js +8 -2
  124. package/dist/src/runtime/steps/supervisor-decision/format.js +32 -20
  125. package/dist/src/runtime/steps/supervisor-decision/input.d.ts +4 -1
  126. package/dist/src/runtime/steps/supervisor-decision/input.js +19 -1
  127. package/dist/src/runtime/steps/supervisor-decision/parser.js +62 -59
  128. package/dist/src/runtime/steps/supervisor-decision/prompt.js +6 -7
  129. package/dist/src/runtime/steps/supervisor-decision/resume.js +47 -1
  130. package/dist/src/runtime/steps/supervisor-decision/run.js +10 -0
  131. package/dist/src/runtime/steps/supervisor-decision/working-directory.js +2 -1
  132. package/dist/src/runtime/steps/worker-decision/contracts.d.ts +2 -1
  133. package/dist/src/runtime/steps/worker-decision/input/canonical-state.js +8 -1
  134. package/dist/src/runtime/steps/worker-decision/input/diagnostic-projection.js +2 -2
  135. package/dist/src/runtime/steps/worker-decision/input/prompt-context.d.ts +1 -0
  136. package/dist/src/runtime/steps/worker-decision/input/prompt-context.js +4 -1
  137. package/dist/src/runtime/steps/worker-decision/input/reference-context.js +34 -14
  138. package/dist/src/runtime/steps/worker-decision/input/types.d.ts +2 -1
  139. package/dist/src/runtime/steps/worker-decision/input.d.ts +1 -0
  140. package/dist/src/runtime/steps/worker-decision/input.js +1 -0
  141. package/dist/src/runtime/steps/worker-decision/payload-dependency-results.d.ts +8 -0
  142. package/dist/src/runtime/steps/worker-decision/payload-dependency-results.js +19 -0
  143. package/dist/src/runtime/steps/worker-decision/prompt.js +8 -8
  144. package/dist/src/runtime/steps/worker-decision/result-author.js +18 -11
  145. package/dist/src/runtime/steps/worker-decision/run.js +2 -2
  146. package/dist/src/web-ui/app/ARCHITECTURE.md +19 -6
  147. package/dist/src/web-ui/app/app.js +6 -7
  148. package/dist/src/web-ui/app/components/composer-context-window.d.ts +25 -0
  149. package/dist/src/web-ui/app/components/composer-context-window.js +155 -0
  150. package/dist/src/web-ui/app/components/composer-plan.d.ts +9 -0
  151. package/dist/src/web-ui/app/components/composer-plan.js +128 -0
  152. package/dist/src/web-ui/app/components/conversation-activity.d.ts +5 -6
  153. package/dist/src/web-ui/app/components/conversation-activity.js +64 -157
  154. package/dist/src/web-ui/app/components/conversation-role-cards.d.ts +23 -0
  155. package/dist/src/web-ui/app/components/conversation-role-cards.js +181 -0
  156. package/dist/src/web-ui/app/components/conversation-sidebar-layout.js +12 -0
  157. package/dist/src/web-ui/app/components/conversation-sources.d.ts +7 -0
  158. package/dist/src/web-ui/app/components/conversation-sources.js +182 -0
  159. package/dist/src/web-ui/app/components/conversation-view.js +34 -6
  160. package/dist/src/web-ui/app/components/model-selector.js +4 -1
  161. package/dist/src/web-ui/app/components/operations-section.js +46 -0
  162. package/dist/src/web-ui/app/components/workspace-shell.js +86 -78
  163. package/dist/src/web-ui/app/controllers/realtime-event-controller.d.ts +1 -0
  164. package/dist/src/web-ui/app/controllers/realtime-event-controller.js +55 -90
  165. package/dist/src/web-ui/app/controllers/runtime-selection-controller.js +2 -0
  166. package/dist/src/web-ui/app/controllers/session-controller.js +17 -12
  167. package/dist/src/web-ui/app/index.html +209 -210
  168. package/dist/src/web-ui/app/lib/composer-plan-model.d.ts +32 -0
  169. package/dist/src/web-ui/app/lib/composer-plan-model.js +108 -0
  170. package/dist/src/web-ui/app/lib/conversation-role-model.d.ts +19 -0
  171. package/dist/src/web-ui/app/lib/conversation-role-model.js +193 -0
  172. package/dist/src/web-ui/app/lib/event-presentation.js +30 -9
  173. package/dist/src/web-ui/app/lib/source-url-policy.d.ts +2 -0
  174. package/dist/src/web-ui/app/lib/source-url-policy.js +31 -0
  175. package/dist/src/web-ui/app/lib/task-progress.d.ts +53 -0
  176. package/dist/src/web-ui/app/lib/task-progress.js +233 -0
  177. package/dist/src/web-ui/app/lib/tool-invocation-count.d.ts +5 -0
  178. package/dist/src/web-ui/app/lib/tool-invocation-count.js +21 -0
  179. package/dist/src/web-ui/app/lib/web-source-event.d.ts +8 -0
  180. package/dist/src/web-ui/app/lib/web-source-event.js +21 -0
  181. package/dist/src/web-ui/app/lib/web-sources.d.ts +36 -0
  182. package/dist/src/web-ui/app/lib/web-sources.js +177 -0
  183. package/dist/src/web-ui/app/styles/00-tokens-base.css +15 -3
  184. package/dist/src/web-ui/app/styles/10-shell-sessions.css +24 -34
  185. package/dist/src/web-ui/app/styles/12-conversation-sidebar.css +93 -0
  186. package/dist/src/web-ui/app/styles/20-chat-composer.css +55 -472
  187. package/dist/src/web-ui/app/styles/21-composer-controls.css +368 -0
  188. package/dist/src/web-ui/app/styles/22-composer-context-window.css +90 -0
  189. package/dist/src/web-ui/app/styles/23-composer-plan.css +171 -0
  190. package/dist/src/web-ui/app/styles/24-conversation-role-cards.css +205 -0
  191. package/dist/src/web-ui/app/styles/25-conversation-sources.css +141 -0
  192. package/dist/src/web-ui/app/styles/30-inspector-config.css +21 -209
  193. package/dist/src/web-ui/app/styles/35-memory-onboarding.css +8 -8
  194. package/dist/src/web-ui/app/styles/36-memory-management.css +13 -13
  195. package/dist/src/web-ui/app/styles/37-config-operations.css +197 -0
  196. package/dist/src/web-ui/app/styles/40-config-workspace.css +36 -36
  197. package/dist/src/web-ui/app/styles/90-responsive.css +14 -77
  198. package/dist/src/web-ui/app/styles/foundation.css +2 -2
  199. package/dist/src/web-ui/app/styles.css +7 -0
  200. package/dist/src/web-ui/app/ui-behavior.d.ts +1 -1
  201. package/dist/src/web-ui/app/ui-behavior.js +1 -1
  202. package/docs/configuration.md +15 -8
  203. package/docs/installation.md +7 -4
  204. package/docs/known-limitations.md +21 -0
  205. package/docs/plugins.md +28 -13
  206. package/docs/runtime-invariant-coverage.md +18 -17
  207. package/docs/runtime-prompt-policy-contracts.md +27 -17
  208. package/package.json +4 -3
  209. package/plugins/capability-brief/source/index.ts +5 -48
  210. package/plugins/capability-brief/src/index.cjs +46 -33
  211. package/plugins/system-probe/plugin.json +1 -0
  212. package/plugins/web/README.md +125 -0
  213. package/plugins/web/plugin.json +1 -1
  214. package/plugins/web/skills/web_fetch_skill/SKILL.md +2 -2
  215. package/plugins/web/skills/web_search_skill/SKILL.md +8 -4
  216. package/plugins/web/source/errors.ts +5 -1
  217. package/plugins/web/source/fetch-presentation.ts +61 -0
  218. package/plugins/web/source/fetch-service.ts +2 -31
  219. package/plugins/web/source/light/config.ts +196 -0
  220. package/plugins/web/source/light/crawl/origin-gate.ts +134 -0
  221. package/plugins/web/source/light/crawl/robots-cache.ts +71 -0
  222. package/plugins/web/source/light/crawl/robots-parser.ts +132 -0
  223. package/plugins/web/source/light/crawl/robots-policy.ts +154 -0
  224. package/plugins/web/source/light/crawl/session-budget.ts +141 -0
  225. package/plugins/web/source/light/crawl/transport.ts +216 -0
  226. package/plugins/web/source/light/discovery/candidate-values.ts +63 -0
  227. package/plugins/web/source/light/discovery/feed-document.ts +127 -0
  228. package/plugins/web/source/light/discovery/html-links.ts +80 -0
  229. package/plugins/web/source/light/discovery/html-robots-directives.ts +29 -0
  230. package/plugins/web/source/light/discovery/markup-tree.ts +145 -0
  231. package/plugins/web/source/light/discovery/origin-dates.ts +71 -0
  232. package/plugins/web/source/light/discovery/sitemap-document.ts +63 -0
  233. package/plugins/web/source/light/documents/cache-policy.ts +28 -0
  234. package/plugins/web/source/light/documents/document-cache.ts +99 -0
  235. package/plugins/web/source/light/documents/document-charset.ts +89 -0
  236. package/plugins/web/source/light/documents/document-contract.ts +20 -0
  237. package/plugins/web/source/light/documents/document-reader.ts +75 -0
  238. package/plugins/web/source/light/documents/extract-document.ts +124 -0
  239. package/plugins/web/source/light/documents/http-cache-freshness.ts +68 -0
  240. package/plugins/web/source/light/documents/response-decoding.ts +120 -0
  241. package/plugins/web/source/light/ranking/document-ranking.ts +114 -0
  242. package/plugins/web/source/light/ranking/query-normalization.ts +69 -0
  243. package/plugins/web/source/light/search-collection.ts +136 -0
  244. package/plugins/web/source/light/search-frontier.ts +246 -0
  245. package/plugins/web/source/light/search-metadata.ts +84 -0
  246. package/plugins/web/source/light/search-service.ts +154 -0
  247. package/plugins/web/source/light/search-unavailable.ts +33 -0
  248. package/plugins/web/source/light/sources/catalog.ts +312 -0
  249. package/plugins/web/source/light/sources/source-definition.ts +9 -0
  250. package/plugins/web/source/light/sources/source-selection.ts +35 -0
  251. package/plugins/web/source/plugin.ts +70 -56
  252. package/plugins/web/source/public-http.ts +10 -1
  253. package/plugins/web/source/search-presentation.ts +63 -63
  254. package/plugins/web/source/search-service-selector.ts +31 -0
  255. package/plugins/web/source/search-service.ts +6 -0
  256. package/plugins/web/source/search-source-presentation.ts +76 -0
  257. package/plugins/web/source/search-types.ts +4 -0
  258. package/plugins/web/source/source-output-receipts.ts +267 -0
  259. package/plugins/web/source/source-receipt-budget.ts +51 -0
  260. package/plugins/web/source/source-receipt-contract.ts +18 -0
  261. package/plugins/web/src/index.cjs +3214 -288
  262. package/src/runtime/README.md +30 -9
  263. package/dist/src/model-gateway/structured-output/budget.d.ts +0 -7
  264. package/dist/src/model-gateway/structured-output/budget.js +0 -20
  265. package/dist/src/model-gateway/structured-output/schema-character-estimator.d.ts +0 -1
  266. package/dist/src/model-gateway/structured-output/schema-character-estimator.js +0 -211
  267. package/dist/src/model-gateway/structured-output-budget.d.ts +0 -1
  268. package/dist/src/model-gateway/structured-output-budget.js +0 -1
@@ -0,0 +1,182 @@
1
+ import {
2
+ parseWebSourceUrl,
3
+ webSourceFaviconUrl,
4
+ } from "../lib/source-url-policy.js";
5
+
6
+ const PRESENTATION_LABELS = {
7
+ content: { short: "Content", detail: "Content returned" },
8
+ snippet: { short: "Snippet", detail: "Snippet returned" },
9
+ reference: { short: "Link", detail: "Reference returned" },
10
+ omitted: { short: "Omitted", detail: "Omitted from tool output" },
11
+ };
12
+
13
+ function sourceStatus(source) {
14
+ if (source.presentation === "unknown") {
15
+ const label = source.retrieval === "retrieved" ? "Fetched" : "Found";
16
+ return { short: label, detail: label };
17
+ }
18
+ return PRESENTATION_LABELS[source.presentation] || { short: "", detail: "" };
19
+ }
20
+
21
+ function sourceDescription(source, url, status) {
22
+ const details = [source.title, url.href, status.detail];
23
+ if (source.retrieval === "failed") details.push("Fetch failed");
24
+ if (source.contentTruncated) details.push("Partial content");
25
+ const requested = parseWebSourceUrl(source.requestedUrl);
26
+ if (requested && requested.href !== url.href) {
27
+ details.push(`Requested: ${requested.href}`);
28
+ }
29
+ return details.filter(Boolean).join("\n");
30
+ }
31
+
32
+ function element(documentRoot, tag, className, text = "") {
33
+ const node = documentRoot.createElement(tag);
34
+ node.className = className;
35
+ node.textContent = text;
36
+ return node;
37
+ }
38
+
39
+ function sourceIcon(documentRoot, url) {
40
+ const icon = element(documentRoot, "span", "conversation-source-icon");
41
+ icon.setAttribute("aria-hidden", "true");
42
+ const hostname = parseWebSourceUrl(url)?.hostname || "";
43
+ const initial =
44
+ hostname
45
+ .replace(/^www\./u, "")
46
+ .charAt(0)
47
+ .toUpperCase() || "↗";
48
+ const fallback = element(
49
+ documentRoot,
50
+ "span",
51
+ "conversation-source-initial",
52
+ initial,
53
+ );
54
+ icon.appendChild(fallback);
55
+ const iconUrl = webSourceFaviconUrl(url);
56
+ if (!iconUrl) return icon;
57
+ const image = documentRoot.createElement("img");
58
+ image.className = "conversation-source-favicon";
59
+ image.alt = "";
60
+ image.width = 16;
61
+ image.height = 16;
62
+ image.loading = "lazy";
63
+ image.decoding = "async";
64
+ image.referrerPolicy = "no-referrer";
65
+ image.addEventListener("load", () => {
66
+ fallback.hidden = true;
67
+ });
68
+ image.addEventListener("error", () => {
69
+ image.hidden = true;
70
+ fallback.hidden = false;
71
+ });
72
+ image.src = iconUrl;
73
+ icon.appendChild(image);
74
+ return icon;
75
+ }
76
+
77
+ function sourceCard(documentRoot, source) {
78
+ const url = parseWebSourceUrl(source.url);
79
+ if (!url) return null;
80
+ const item = element(documentRoot, "li", "conversation-source-card");
81
+ const link = element(documentRoot, "a", "conversation-source-link");
82
+ const status = sourceStatus(source);
83
+ const description = sourceDescription(source, url, status);
84
+ link.href = url.href;
85
+ link.target = "_blank";
86
+ link.rel = "noopener noreferrer";
87
+ link.title = description;
88
+ link.setAttribute("aria-label", description);
89
+ link.appendChild(sourceIcon(documentRoot, url.href));
90
+ const domain = element(
91
+ documentRoot,
92
+ "span",
93
+ "conversation-source-domain",
94
+ url.hostname,
95
+ );
96
+ domain.dir = "ltr";
97
+ link.appendChild(domain);
98
+ const failed = source.retrieval === "failed";
99
+ const label = failed ? `${status.short} · failed` : status.short;
100
+ const badge = element(
101
+ documentRoot,
102
+ "span",
103
+ "conversation-source-status",
104
+ label,
105
+ );
106
+ badge.setAttribute("aria-hidden", "true");
107
+ if (failed) badge.classList.add("is-failed");
108
+ link.appendChild(badge);
109
+ item.appendChild(link);
110
+ return item;
111
+ }
112
+
113
+ export function createConversationSources({ documentRoot = document } = {}) {
114
+ function createNode(groups = []) {
115
+ if (!Array.isArray(groups) || groups.length === 0) return null;
116
+ const section = element(documentRoot, "section", "conversation-sources");
117
+ section.setAttribute("aria-label", "Web sources");
118
+ const heading = element(
119
+ documentRoot,
120
+ "div",
121
+ "conversation-sources-heading",
122
+ );
123
+ heading.appendChild(
124
+ element(documentRoot, "h3", "conversation-sources-title", "Web sources"),
125
+ );
126
+ heading.title = "What the tools returned";
127
+ section.appendChild(heading);
128
+
129
+ groups.forEach((group, index) => {
130
+ const block = element(documentRoot, "div", "conversation-sources-group");
131
+ const header = element(
132
+ documentRoot,
133
+ "div",
134
+ "conversation-sources-group-header",
135
+ );
136
+ const operation = group.operation === "fetch" ? "Fetch" : "Search";
137
+ header.appendChild(
138
+ element(
139
+ documentRoot,
140
+ "h4",
141
+ "conversation-sources-operation",
142
+ `${operation} · ${index + 1}`,
143
+ ),
144
+ );
145
+ if (group.provider) {
146
+ const provider = group.provider === "light" ? "Light" : "Brave";
147
+ header.appendChild(
148
+ element(
149
+ documentRoot,
150
+ "span",
151
+ "conversation-sources-provider",
152
+ provider,
153
+ ),
154
+ );
155
+ }
156
+ const list = element(documentRoot, "ol", "conversation-sources-list");
157
+ for (const source of group.sources) {
158
+ const card = sourceCard(documentRoot, source);
159
+ if (card) list.appendChild(card);
160
+ }
161
+ block.append(header, list);
162
+ if (group.omittedSourceCount > 0) {
163
+ const notice = element(
164
+ documentRoot,
165
+ "p",
166
+ "conversation-sources-omitted",
167
+ `${group.omittedSourceCount} additional sources unavailable in this display`,
168
+ );
169
+ notice.title =
170
+ "Source metadata exceeded the result size limit. Tool output was preserved.";
171
+ notice.setAttribute(
172
+ "aria-label",
173
+ `${notice.textContent}. ${notice.title}`,
174
+ );
175
+ block.appendChild(notice);
176
+ }
177
+ section.appendChild(block);
178
+ });
179
+ return section;
180
+ }
181
+ return { createNode };
182
+ }
@@ -1,15 +1,19 @@
1
1
  import { isNearScrollEnd, pinScrollToEnd } from "../ui-behavior.js";
2
2
  import { applyTextDirection, renderMarkdown } from "../lib/text-format.js";
3
+ import { createConversationActivity } from "./conversation-activity.js";
4
+ import { createComposerPlan } from "./composer-plan.js";
5
+ import { buildComposerPlanModel } from "../lib/composer-plan-model.js";
3
6
  import {
4
- createConversationActivity,
5
- createConversationContextWindow,
6
- } from "./conversation-activity.js";
7
+ buildComposerContextWindowModel,
8
+ createComposerContextWindow,
9
+ } from "./composer-context-window.js";
7
10
  import { createMessageAttachments } from "./message-attachments.js";
8
11
  import { createMessageTimestamp } from "./message-timestamp.js";
9
12
 
10
13
  export function createConversationView({
11
14
  dom,
12
15
  getMessages,
16
+ getActiveRequestId = () => "",
13
17
  getActivityForMessage,
14
18
  getPendingApproval,
15
19
  createApprovalCard,
@@ -20,7 +24,12 @@ export function createConversationView({
20
24
  viewport = window,
21
25
  }) {
22
26
  const conversationActivity = createConversationActivity({ documentRoot });
23
- const conversationContextWindow = createConversationContextWindow({
27
+ const composerContextWindow = createComposerContextWindow({
28
+ container: dom.composerContextWindow,
29
+ documentRoot,
30
+ });
31
+ const composerPlan = createComposerPlan({
32
+ container: dom.composerPlan,
24
33
  documentRoot,
25
34
  });
26
35
  const messageAttachments = createMessageAttachments({
@@ -132,8 +141,6 @@ export function createConversationView({
132
141
  streaming: Boolean(message.streaming),
133
142
  ...getActivityForMessage(message),
134
143
  };
135
- const contextWindow = conversationContextWindow.createNode(activityInput);
136
- if (contextWindow) bubble.appendChild(contextWindow);
137
144
  const activity = conversationActivity.createNode(activityInput);
138
145
  if (activity) bubble.appendChild(activity);
139
146
  }
@@ -185,7 +192,25 @@ export function createConversationView({
185
192
  return row;
186
193
  }
187
194
 
195
+ function renderContextWindow() {
196
+ composerContextWindow.render(
197
+ buildComposerContextWindowModel({
198
+ messages: getMessages(),
199
+ activeRequestId: getActiveRequestId(),
200
+ getActivityForMessage,
201
+ }),
202
+ );
203
+ }
204
+
188
205
  function render() {
206
+ renderContextWindow();
207
+ composerPlan.render(
208
+ buildComposerPlanModel({
209
+ messages: getMessages(),
210
+ activeRequestId: getActiveRequestId(),
211
+ getActivityForMessage,
212
+ }),
213
+ );
189
214
  const previousScrollTop = dom.messagesList.scrollTop;
190
215
  const shouldFollow =
191
216
  viewState.followMessages || isNearScrollEnd(dom.messagesList);
@@ -248,6 +273,8 @@ export function createConversationView({
248
273
  cancelScheduledMessageRender();
249
274
  cancelScheduledThinkingRender();
250
275
  conversationActivity.reset();
276
+ composerContextWindow.reset();
277
+ composerPlan.reset();
251
278
  }
252
279
 
253
280
  function forgetThinkingDisclosure(messageId) {
@@ -271,6 +298,7 @@ export function createConversationView({
271
298
  cancelScheduledThinkingRender,
272
299
  forgetThinkingDisclosure,
273
300
  render,
301
+ renderContextWindow,
274
302
  reset,
275
303
  scheduleMessageRender,
276
304
  scheduleThinkingRender,
@@ -50,7 +50,10 @@ export function createModelSelector({
50
50
  button.disabled = select.disabled || options.length === 0;
51
51
  button.setAttribute("aria-expanded", open ? "true" : "false");
52
52
  button.classList.toggle("open", open);
53
- valueLabel.textContent = selected?.label || "No models configured";
53
+ const label = selected?.label || "No models configured";
54
+ valueLabel.textContent = label;
55
+ button.title = `Model: ${label}`;
56
+ button.setAttribute("aria-label", `Model: ${label}`);
54
57
  menu.hidden = !open;
55
58
  menu.classList.toggle("open", open);
56
59
  menu.setAttribute("role", "menu");
@@ -0,0 +1,46 @@
1
+ import { wrappedIndex } from "../ui-behavior.js";
2
+ import { textOf } from "../lib/text-format.js";
3
+
4
+ export function createOperationsSection({ buttons, pages }) {
5
+ const availableTabs = new Set(
6
+ buttons.map((button) => textOf(button.dataset.tab)),
7
+ );
8
+
9
+ function activateTab(tabName) {
10
+ const normalized = textOf(tabName, "runtime") || "runtime";
11
+ const nextTab = availableTabs.has(normalized) ? normalized : "runtime";
12
+ for (const button of buttons) {
13
+ const active = button.dataset.tab === nextTab;
14
+ button.classList.toggle("active", active);
15
+ button.setAttribute("aria-selected", active ? "true" : "false");
16
+ button.tabIndex = active ? 0 : -1;
17
+ }
18
+ for (const page of pages) {
19
+ const active = page.id === `${nextTab}Tab`;
20
+ page.classList.toggle("active", active);
21
+ page.hidden = !active;
22
+ page.inert = !active;
23
+ }
24
+ }
25
+
26
+ function navigateTab(event, button) {
27
+ const isArrowNavigation =
28
+ event.key === "ArrowLeft" || event.key === "ArrowRight";
29
+ if (!isArrowNavigation) return;
30
+ event.preventDefault();
31
+ const delta = event.key === "ArrowRight" ? 1 : -1;
32
+ const next =
33
+ buttons[wrappedIndex(buttons.indexOf(button), delta, buttons.length)];
34
+ next?.focus();
35
+ activateTab(next?.dataset.tab);
36
+ }
37
+
38
+ function bind() {
39
+ for (const button of buttons) {
40
+ button.addEventListener("click", () => activateTab(button.dataset.tab));
41
+ button.addEventListener("keydown", (event) => navigateTab(event, button));
42
+ }
43
+ }
44
+
45
+ return { activateTab, bind };
46
+ }
@@ -2,9 +2,13 @@ import {
2
2
  createInitialWorkspaceShellState,
3
3
  normalizeWorkspaceDestination,
4
4
  toggleWorkspaceSheet,
5
- wrappedIndex,
6
5
  } from "../ui-behavior.js";
7
6
  import { textOf } from "../lib/text-format.js";
7
+ import {
8
+ CONVERSATION_SIDEBAR_MEDIA_QUERY,
9
+ resolveConversationSidebarLayout,
10
+ } from "./conversation-sidebar-layout.js";
11
+ import { createOperationsSection } from "./operations-section.js";
8
12
 
9
13
  export function createWorkspaceShell({
10
14
  dom,
@@ -14,37 +18,45 @@ export function createWorkspaceShell({
14
18
  }) {
15
19
  const shellState = {
16
20
  ...createInitialWorkspaceShellState(),
17
- activeOperationsTab: "runtime",
18
21
  toastTimer: 0,
19
22
  bound: false,
20
23
  };
24
+ const sidebarViewport = viewport.matchMedia?.(
25
+ CONVERSATION_SIDEBAR_MEDIA_QUERY,
26
+ );
27
+ const operationsSection = createOperationsSection({
28
+ buttons: dom.operationsTabButtons,
29
+ pages: dom.operationsTabPages,
30
+ });
31
+
32
+ function sidebarLayout() {
33
+ return resolveConversationSidebarLayout(
34
+ shellState.workspace,
35
+ shellState.activeSheet,
36
+ sidebarViewport?.matches === true,
37
+ );
38
+ }
21
39
 
22
40
  function setCurrentPage(button, active) {
23
41
  if (active) {
24
42
  button.setAttribute("aria-current", "page");
25
- } else {
26
- button.removeAttribute("aria-current");
43
+ return;
27
44
  }
45
+ button.removeAttribute("aria-current");
28
46
  }
29
47
 
30
48
  function syncShell() {
31
49
  const chatWorkspace = shellState.workspace === "chat";
32
- const operationsWorkspace = shellState.workspace === "operations";
33
50
  const configWorkspace = shellState.workspace === "config";
34
- const sessionsOpen = chatWorkspace && shellState.activeSheet === "sessions";
51
+ const sessions = sidebarLayout();
52
+ const showSessionsToggle = chatWorkspace && !sessions.docked;
35
53
 
36
- dom.app.classList.toggle("operations-workspace", operationsWorkspace);
37
54
  dom.app.classList.toggle("config-workspace", configWorkspace);
38
- dom.app.classList.toggle("sessions-open", sessionsOpen);
55
+ dom.app.classList.toggle("sessions-open", sessions.drawerOpen);
56
+ dom.app.classList.toggle("sessions-docked", sessions.docked);
39
57
 
40
58
  dom.chatPanel.hidden = !chatWorkspace;
41
- dom.chatPanel.inert = !chatWorkspace || sessionsOpen;
42
- dom.operationsWorkspacePanel.hidden = !operationsWorkspace;
43
- dom.operationsWorkspacePanel.inert = !operationsWorkspace;
44
- dom.operationsWorkspacePanel.setAttribute(
45
- "aria-hidden",
46
- operationsWorkspace ? "false" : "true",
47
- );
59
+ dom.chatPanel.inert = !chatWorkspace || sessions.drawerOpen;
48
60
  dom.configWorkspacePanel.hidden = !configWorkspace;
49
61
  dom.configWorkspacePanel.inert = !configWorkspace;
50
62
  dom.configWorkspacePanel.setAttribute(
@@ -52,37 +64,72 @@ export function createWorkspaceShell({
52
64
  configWorkspace ? "false" : "true",
53
65
  );
54
66
 
55
- dom.sessionsPanel.hidden = !sessionsOpen;
56
- dom.sessionsPanel.inert = !sessionsOpen;
67
+ dom.sessionsPanel.hidden = !sessions.visible;
68
+ dom.sessionsPanel.inert = !sessions.visible;
57
69
  dom.sessionsPanel.setAttribute(
58
70
  "aria-hidden",
59
- sessionsOpen ? "false" : "true",
71
+ sessions.visible ? "false" : "true",
60
72
  );
61
73
  dom.sessionsToggleButton.setAttribute(
62
74
  "aria-expanded",
63
- sessionsOpen ? "true" : "false",
75
+ sessions.visible ? "true" : "false",
64
76
  );
65
77
  dom.sessionsToggleButton.setAttribute(
66
78
  "aria-label",
67
- sessionsOpen ? "Close conversations" : "Open conversations",
79
+ sessions.drawerOpen ? "Close conversations" : "Open conversations",
68
80
  );
81
+ dom.sessionsToggleButton.hidden = !showSessionsToggle;
82
+ dom.closeSessionsButton.hidden = sessions.docked;
69
83
 
70
84
  setCurrentPage(dom.chatWorkspaceButton, chatWorkspace);
71
- setCurrentPage(dom.operationsWorkspaceButton, operationsWorkspace);
72
85
  setCurrentPage(dom.configWorkspaceButton, configWorkspace);
73
86
 
74
- dom.panelBackdrop.classList.toggle("visible", sessionsOpen);
75
- dom.panelBackdrop.tabIndex = sessionsOpen ? 0 : -1;
87
+ dom.panelBackdrop.classList.toggle("visible", sessions.drawerOpen);
88
+ dom.panelBackdrop.tabIndex = sessions.drawerOpen ? 0 : -1;
76
89
  dom.panelBackdrop.setAttribute(
77
90
  "aria-hidden",
78
- sessionsOpen ? "false" : "true",
91
+ sessions.drawerOpen ? "false" : "true",
79
92
  );
80
93
  }
81
94
 
82
- function workspaceBackButton(destination) {
83
- return destination === "operations"
84
- ? dom.closeOperationsWorkspaceButton
85
- : dom.closeConfigWorkspaceButton;
95
+ function focusVisibleSessionsControl() {
96
+ const sessions = sidebarLayout();
97
+ if (!sessions.visible) return;
98
+ if (sessions.drawerOpen) {
99
+ dom.closeSessionsButton.focus();
100
+ return;
101
+ }
102
+ const sidebarControl = dom.sessionSearchInput ?? dom.refreshSessionsButton;
103
+ (sidebarControl ?? dom.chatWorkspaceButton).focus();
104
+ }
105
+
106
+ function sidebarFocusNeedsRestoration(activeElement, sessions) {
107
+ if (!activeElement) return false;
108
+ if (activeElement === dom.sessionsToggleButton) {
109
+ return dom.sessionsToggleButton.hidden;
110
+ }
111
+ if (activeElement === dom.closeSessionsButton) {
112
+ return dom.closeSessionsButton.hidden;
113
+ }
114
+ if (activeElement === dom.panelBackdrop) return !sessions.drawerOpen;
115
+ if (sessions.visible) return false;
116
+ return Boolean(activeElement.closest?.(".sessions-panel"));
117
+ }
118
+
119
+ function syncSidebarViewport() {
120
+ const activeElement = documentRoot.activeElement;
121
+ shellState.activeSheet = "";
122
+ syncShell();
123
+ const sessions = sidebarLayout();
124
+ if (!sidebarFocusNeedsRestoration(activeElement, sessions)) return;
125
+ if (sessions.docked) {
126
+ focusVisibleSessionsControl();
127
+ return;
128
+ }
129
+ const visibleNavigationButton = dom.sessionsToggleButton.hidden
130
+ ? dom.chatWorkspaceButton
131
+ : dom.sessionsToggleButton;
132
+ visibleNavigationButton.focus();
86
133
  }
87
134
 
88
135
  function prepareWorkspaceTransition(destination) {
@@ -113,9 +160,10 @@ export function createWorkspaceShell({
113
160
 
114
161
  if (options.focus === false) return true;
115
162
  if (nextWorkspace !== "chat") {
116
- viewport.requestAnimationFrame(() =>
117
- workspaceBackButton(nextWorkspace)?.focus(),
118
- );
163
+ viewport.requestAnimationFrame(() => {
164
+ if (shellState.workspace !== nextWorkspace) return;
165
+ dom.closeConfigWorkspaceButton.focus();
166
+ });
119
167
  return true;
120
168
  }
121
169
  if (previousWorkspace !== "chat" || previousSheet) {
@@ -147,12 +195,13 @@ export function createWorkspaceShell({
147
195
  preparedTransition.commitBeforeChange();
148
196
  const wasOpen = shellState.activeSheet === "sessions";
149
197
  shellState.workspace = "chat";
150
- shellState.activeSheet = open ? "sessions" : "";
198
+ const useDrawer = open && sidebarViewport?.matches !== true;
199
+ shellState.activeSheet = useDrawer ? "sessions" : "";
151
200
  syncShell();
152
201
 
153
202
  if (options.focus === false) return true;
154
203
  if (open) {
155
- viewport.requestAnimationFrame(() => dom.closeSessionsButton?.focus());
204
+ viewport.requestAnimationFrame(focusVisibleSessionsControl);
156
205
  return true;
157
206
  }
158
207
  if (
@@ -172,26 +221,6 @@ export function createWorkspaceShell({
172
221
  });
173
222
  }
174
223
 
175
- function activateOperationsTab(tabName) {
176
- const availableTabs = new Set(
177
- dom.operationsTabButtons.map((button) => textOf(button.dataset.tab)),
178
- );
179
- const normalized = textOf(tabName, "runtime") || "runtime";
180
- const nextTab = availableTabs.has(normalized) ? normalized : "runtime";
181
- shellState.activeOperationsTab = nextTab;
182
- for (const button of dom.operationsTabButtons) {
183
- const active = button.dataset.tab === nextTab;
184
- button.classList.toggle("active", active);
185
- button.setAttribute("aria-selected", active ? "true" : "false");
186
- button.tabIndex = active ? 0 : -1;
187
- }
188
- for (const page of dom.operationsTabPages) {
189
- const active = page.id === `${nextTab}Tab`;
190
- page.classList.toggle("active", active);
191
- page.hidden = !active;
192
- }
193
- }
194
-
195
224
  function showToast(message, tone = "") {
196
225
  viewport.clearTimeout(shellState.toastTimer);
197
226
  dom.toastRegion.textContent = textOf(message);
@@ -218,15 +247,9 @@ export function createWorkspaceShell({
218
247
  dom.chatWorkspaceButton.addEventListener("click", () => {
219
248
  activateWorkspace("chat");
220
249
  });
221
- dom.operationsWorkspaceButton.addEventListener("click", () => {
222
- activateWorkspace("operations");
223
- });
224
250
  dom.configWorkspaceButton.addEventListener("click", () => {
225
251
  activateWorkspace("config");
226
252
  });
227
- dom.closeOperationsWorkspaceButton.addEventListener("click", () => {
228
- activateWorkspace("chat");
229
- });
230
253
  dom.closeConfigWorkspaceButton.addEventListener("click", () => {
231
254
  activateWorkspace("chat");
232
255
  });
@@ -239,33 +262,18 @@ export function createWorkspaceShell({
239
262
  setSessionsDrawerOpen(false, { restoreFocus: true });
240
263
  }
241
264
  });
242
- for (const button of dom.operationsTabButtons) {
243
- button.addEventListener("click", () => {
244
- activateOperationsTab(button.dataset.tab);
245
- });
246
- button.addEventListener("keydown", (event) => {
247
- if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
248
- event.preventDefault();
249
- const currentIndex = dom.operationsTabButtons.indexOf(button);
250
- const delta = event.key === "ArrowRight" ? 1 : -1;
251
- const next =
252
- dom.operationsTabButtons[
253
- wrappedIndex(currentIndex, delta, dom.operationsTabButtons.length)
254
- ];
255
- next?.focus();
256
- activateOperationsTab(next?.dataset.tab);
257
- });
258
- }
265
+ sidebarViewport?.addEventListener("change", syncSidebarViewport);
266
+ operationsSection.bind();
259
267
  }
260
268
 
261
269
  function load() {
262
270
  Object.assign(shellState, createInitialWorkspaceShellState());
263
- activateOperationsTab("runtime");
271
+ operationsSection.activateTab("runtime");
264
272
  syncShell();
265
273
  }
266
274
 
267
275
  return {
268
- activateOperationsTab,
276
+ activateOperationsTab: operationsSection.activateTab,
269
277
  activateWorkspace,
270
278
  bind,
271
279
  closeOverlaysOnEscape,
@@ -26,6 +26,7 @@ export interface RealtimeEventControllerOptions {
26
26
  message: RealtimeEventRecord,
27
27
  ): ConversationSessionMessage;
28
28
  renderMessages(): void;
29
+ renderContextWindow?(): void;
29
30
  scheduleMessageRender(): void;
30
31
  scheduleThinkingRender(): void;
31
32
  cancelScheduledMessageRender(): void;