@workflow-code/cli 0.2.4-20261012006.codex-studio-integration → 0.2.4-20261012007.codex-studio-integration

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.
@@ -1072,23 +1072,89 @@ main {
1072
1072
  margin-top: 12px;
1073
1073
  }
1074
1074
  .resource-card {
1075
- display: flex;
1076
- align-items: center;
1077
- gap: 8px;
1075
+ display: block;
1076
+ width: 100%;
1078
1077
  min-width: 0;
1079
- max-width: 100%;
1080
- min-height: 36px;
1081
- padding: 6px 10px;
1078
+ margin: 8px 0;
1082
1079
  border: 1px solid var(--border);
1083
1080
  border-radius: 6px;
1084
1081
  background: var(--vscode-editor-background);
1082
+ overflow: hidden;
1083
+ container-type: inline-size;
1084
+ }
1085
+ .resource-card-row {
1086
+ display: flex;
1087
+ align-items: center;
1088
+ gap: 4px;
1089
+ min-height: 44px;
1090
+ padding: 4px 8px;
1091
+ }
1092
+ .resource-target {
1093
+ display: flex;
1094
+ align-items: center;
1095
+ gap: 10px;
1096
+ flex: 1;
1097
+ min-width: 0;
1098
+ min-height: 36px;
1099
+ padding: 6px 4px;
1100
+ border: 0;
1101
+ background: transparent;
1085
1102
  text-align: left;
1086
1103
  }
1087
- .resource-card > span:not(.codicon) {
1104
+ .resource-target > .codicon {
1105
+ flex: none;
1106
+ color: var(--muted);
1107
+ }
1108
+ .resource-title {
1088
1109
  min-width: 0;
1089
- overflow: hidden;
1090
- text-overflow: ellipsis;
1091
- white-space: nowrap;
1110
+ white-space: normal;
1111
+ overflow-wrap: anywhere;
1112
+ line-height: 1.5;
1113
+ }
1114
+ .resource-card-row > .icon-button {
1115
+ flex: none;
1116
+ width: 28px;
1117
+ height: 28px;
1118
+ }
1119
+ @container (max-width: 280px) {
1120
+ .resource-card-row {
1121
+ flex-wrap: wrap;
1122
+ }
1123
+ .resource-target {
1124
+ flex-basis: 100%;
1125
+ }
1126
+ }
1127
+ .resource-inline {
1128
+ height: 360px;
1129
+ max-height: 70vh;
1130
+ min-height: 160px;
1131
+ border-top: 1px solid var(--border);
1132
+ overflow: auto;
1133
+ }
1134
+ .resource-inline[role] {
1135
+ padding: 12px;
1136
+ color: var(--muted);
1137
+ }
1138
+ .resource-inline[role=alert] {
1139
+ color: var(--vscode-errorForeground);
1140
+ }
1141
+ .resource-inline iframe {
1142
+ display: block;
1143
+ width: 100%;
1144
+ height: 100%;
1145
+ border: 0;
1146
+ }
1147
+ .resource-call-details {
1148
+ padding: 8px 12px;
1149
+ border-top: 1px solid var(--border);
1150
+ max-height: 320px;
1151
+ overflow: auto;
1152
+ }
1153
+ .resource-call-details pre {
1154
+ margin: 0;
1155
+ font-size: 11px;
1156
+ white-space: pre-wrap;
1157
+ overflow-wrap: anywhere;
1092
1158
  }
1093
1159
  .input-prompt {
1094
1160
  margin: 16px 0;
@@ -16414,7 +16414,33 @@
16414
16414
  "Write file": "\u5199\u5165\u6587\u4EF6",
16415
16415
  Yes: "\u662F",
16416
16416
  "Zoom in": "\u653E\u5927",
16417
- "Zoom out": "\u7F29\u5C0F"
16417
+ "Zoom out": "\u7F29\u5C0F",
16418
+ "Open resource": "\u6253\u5F00\u8D44\u6E90",
16419
+ "Show in conversation": "\u5728\u5BF9\u8BDD\u4E2D\u5C55\u793A",
16420
+ "Hide preview": "\u6536\u8D77\u9884\u89C8",
16421
+ "Open in new window": "\u5728\u65B0\u7A97\u53E3\u4E2D\u6253\u5F00",
16422
+ "Tool call details": "\u5DE5\u5177\u8C03\u7528\u8BE6\u60C5",
16423
+ "Please sign in to preview this server resource.": "\u8BF7\u767B\u5F55\u540E\u9884\u89C8\u670D\u52A1\u5668\u8D44\u6E90\u3002",
16424
+ "You do not have access to this resource.": "\u4F60\u6CA1\u6709\u6B64\u8D44\u6E90\u7684\u8BBF\u95EE\u6743\u9650\u3002",
16425
+ "The resource project is unavailable.": "\u8D44\u6E90\u6240\u5C5E\u9879\u76EE\u4E0D\u53EF\u7528\u3002",
16426
+ "The resource project is unavailable. Import or open the target project first.": "\u8D44\u6E90\u6240\u5C5E\u9879\u76EE\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5148\u5BFC\u5165\u6216\u6253\u5F00\u76EE\u6807\u9879\u76EE\u3002",
16427
+ "The project does not allow this storage location.": "\u9879\u76EE\u4E0D\u5141\u8BB8\u4F7F\u7528\u6B64\u5B58\u50A8\u4F4D\u7F6E\u3002",
16428
+ "This resource no longer exists.": "\u6B64\u8D44\u6E90\u5DF2\u4E0D\u5B58\u5728\u3002",
16429
+ "Server source files cannot be previewed by this card.": "\u6B64\u5361\u7247\u4E0D\u652F\u6301\u9884\u89C8\u670D\u52A1\u5668\u6E90\u7801\u6587\u4EF6\u3002",
16430
+ "File is outside this project.": "\u6587\u4EF6\u4E0D\u5728\u6B64\u9879\u76EE\u4E2D\u3002",
16431
+ "File preview exceeds 2 MiB.": "\u6587\u4EF6\u9884\u89C8\u5927\u5C0F\u8D85\u8FC7 2 MiB\u3002",
16432
+ "This project does not provide a Kanban preview.": "\u6B64\u9879\u76EE\u4E0D\u63D0\u4F9B\u770B\u677F\u9884\u89C8\u3002",
16433
+ "Invalid resource preview URL.": "\u8D44\u6E90\u9884\u89C8\u5730\u5740\u65E0\u6548\u3002",
16434
+ "This preview configuration no longer exists.": "\u6B64\u9884\u89C8\u914D\u7F6E\u5DF2\u4E0D\u5B58\u5728\u3002",
16435
+ "Resource access changed. Open the card again.": "\u8D44\u6E90\u8BBF\u95EE\u72B6\u6001\u5DF2\u53D8\u5316\uFF0C\u8BF7\u91CD\u65B0\u6253\u5F00\u5361\u7247\u3002",
16436
+ "This resource preview is read-only.": "\u6B64\u8D44\u6E90\u9884\u89C8\u4E3A\u53EA\u8BFB\u3002",
16437
+ "Invalid resource action.": "\u8D44\u6E90\u64CD\u4F5C\u65E0\u6548\u3002",
16438
+ "Run no longer exists.": "\u6B64\u8FD0\u884C\u5DF2\u4E0D\u5B58\u5728\u3002",
16439
+ "Conversation changed.": "\u4F1A\u8BDD\u5DF2\u53D8\u5316\u3002",
16440
+ "Unable to load this preview. Retry or open it separately.": "\u65E0\u6CD5\u52A0\u8F7D\u6B64\u9884\u89C8\uFF0C\u8BF7\u91CD\u8BD5\u6216\u5355\u72EC\u6253\u5F00\u3002",
16441
+ "Open in browser": "\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00",
16442
+ "No resources": "\u6682\u65E0\u8D44\u6E90",
16443
+ "Allow popups to open this resource in a new window.": "\u8BF7\u5141\u8BB8\u5F39\u51FA\u7A97\u53E3\u4EE5\u5355\u72EC\u6253\u5F00\u6B64\u8D44\u6E90\u3002"
16418
16444
  };
16419
16445
 
16420
16446
  // src/web/localization.ts
@@ -18877,11 +18903,14 @@ ${state2.user?.avatarUrl ?? ""}`;
18877
18903
  var ConnectionError = class extends Error {
18878
18904
  };
18879
18905
  var BrowserConnection = class {
18880
- constructor(emit) {
18906
+ constructor(emit, live = true) {
18881
18907
  this.emit = emit;
18908
+ this.live = live;
18882
18909
  if (this.token) {
18910
+ const query = new URLSearchParams(location.search);
18911
+ query.delete("session");
18883
18912
  sessionStorage.setItem("workflow-web-session", this.token);
18884
- history.replaceState(null, "", `${location.pathname}${location.search}`);
18913
+ history.replaceState(null, "", `${location.pathname}${query.size ? `?${query}` : ""}`);
18885
18914
  }
18886
18915
  queueMicrotask(() => {
18887
18916
  this.banner = document.createElement("div");
@@ -18915,6 +18944,7 @@ ${state2.user?.avatarUrl ?? ""}`;
18915
18944
  });
18916
18945
  }
18917
18946
  emit;
18947
+ live;
18918
18948
  clientId = crypto.randomUUID();
18919
18949
  source;
18920
18950
  banner;
@@ -18922,10 +18952,11 @@ ${state2.user?.avatarUrl ?? ""}`;
18922
18952
  handshake;
18923
18953
  epoch = 0;
18924
18954
  retry;
18955
+ snapshotTimer;
18925
18956
  status = "connecting";
18926
18957
  attempts = 0;
18927
18958
  closed = false;
18928
- token = new URLSearchParams(location.hash.slice(1)).get("session") ?? sessionStorage.getItem("workflow-web-session");
18959
+ token = new URLSearchParams(location.hash.slice(1)).get("session") ?? new URLSearchParams(location.search).get("session") ?? sessionStorage.getItem("workflow-web-session");
18929
18960
  update(status) {
18930
18961
  this.status = status;
18931
18962
  this.emit({ type: "connection", status });
@@ -18948,6 +18979,7 @@ ${state2.user?.avatarUrl ?? ""}`;
18948
18979
  this.source?.close();
18949
18980
  this.source = void 0;
18950
18981
  clearTimeout(this.retry);
18982
+ clearTimeout(this.snapshotTimer);
18951
18983
  }
18952
18984
  interrupted() {
18953
18985
  this.disconnect();
@@ -19001,6 +19033,10 @@ ${state2.user?.avatarUrl ?? ""}`;
19001
19033
  if (!session.ok) {
19002
19034
  throw new Error("Cannot connect to the project.");
19003
19035
  }
19036
+ if (!this.live) {
19037
+ await this.snapshot(epoch);
19038
+ return;
19039
+ }
19004
19040
  const eventsUrl = new URL(this.fileUrl("/api/events"), location.href);
19005
19041
  eventsUrl.searchParams.set("clientId", this.clientId);
19006
19042
  const source = this.source = new EventSource(eventsUrl.href);
@@ -19037,6 +19073,39 @@ ${state2.user?.avatarUrl ?? ""}`;
19037
19073
  });
19038
19074
  return this.connecting;
19039
19075
  }
19076
+ async snapshot(epoch) {
19077
+ if (epoch !== this.epoch || this.closed) {
19078
+ return;
19079
+ }
19080
+ const response = await fetch("/api/resource-state", { headers: this.headers(), signal: AbortSignal.timeout(1e4) });
19081
+ if (epoch !== this.epoch || this.closed) {
19082
+ return;
19083
+ }
19084
+ if (response.status === 401) {
19085
+ this.disconnect();
19086
+ this.update("expired");
19087
+ return;
19088
+ }
19089
+ if (!response.ok) {
19090
+ throw new Error("Cannot connect to the project.");
19091
+ }
19092
+ const state2 = await response.json();
19093
+ if (epoch !== this.epoch || this.closed) {
19094
+ return;
19095
+ }
19096
+ if (this.status !== "connected") {
19097
+ this.attempts = 0;
19098
+ this.update("connected");
19099
+ }
19100
+ this.emit(state2);
19101
+ this.snapshotTimer = setTimeout(() => {
19102
+ void this.snapshot(epoch).catch(() => {
19103
+ if (epoch === this.epoch && !this.closed) {
19104
+ this.interrupted();
19105
+ }
19106
+ });
19107
+ }, 5e3);
19108
+ }
19040
19109
  async request(message) {
19041
19110
  await this.connecting;
19042
19111
  if (this.status !== "connected") {
@@ -19082,7 +19151,12 @@ ${state2.user?.avatarUrl ?? ""}`;
19082
19151
  return this.token ? { Authorization: `Bearer ${this.token}` } : {};
19083
19152
  }
19084
19153
  fileUrl(path) {
19085
- return this.token ? `${path}?session=${encodeURIComponent(this.token)}` : path;
19154
+ if (!this.token) {
19155
+ return path;
19156
+ }
19157
+ const url = new URL(path, location.href);
19158
+ url.searchParams.set("session", this.token);
19159
+ return `${url.pathname}${url.search}${url.hash}`;
19086
19160
  }
19087
19161
  publicFiles(value) {
19088
19162
  if (Array.isArray(value)) {
@@ -19303,7 +19377,7 @@ ${state2.user?.avatarUrl ?? ""}`;
19303
19377
  };
19304
19378
 
19305
19379
  // src/web/ui/host.ts
19306
- function acquireWorkflowHost() {
19380
+ function acquireWorkflowHost(options = {}) {
19307
19381
  const embedded = globalThis.acquireVsCodeApi;
19308
19382
  if (embedded) {
19309
19383
  return embedded();
@@ -19323,6 +19397,7 @@ ${state2.user?.avatarUrl ?? ""}`;
19323
19397
  window.parent.postMessage(message, "*");
19324
19398
  }
19325
19399
  });
19400
+ let resourceHost = false;
19326
19401
  const clipboard = new ClipboardBridge(
19327
19402
  (message) => window.parent.postMessage(message, "*"),
19328
19403
  (text3) => navigator.clipboard.writeText(text3),
@@ -19335,7 +19410,7 @@ ${state2.user?.avatarUrl ?? ""}`;
19335
19410
  }
19336
19411
  trace.receive(message);
19337
19412
  emit(message);
19338
- });
19413
+ }, options.live);
19339
19414
  const request = async (message) => {
19340
19415
  for (const entry of await connection.request(message)) {
19341
19416
  handleReply(entry);
@@ -19354,6 +19429,12 @@ ${state2.user?.avatarUrl ?? ""}`;
19354
19429
  if (event.data.clipboardVersion !== void 0) {
19355
19430
  clipboard.enable(event.data.clipboardVersion);
19356
19431
  }
19432
+ if (event.data.resourceVersion === 1) {
19433
+ resourceHost = true;
19434
+ }
19435
+ if (event.data.resourceError) {
19436
+ emit(event.data.resourceError);
19437
+ }
19357
19438
  clipboard.receive(event.data.clipboard);
19358
19439
  const traceCommand = trace.command(event.data.traceCommand);
19359
19440
  if (traceCommand) {
@@ -19371,7 +19452,14 @@ ${state2.user?.avatarUrl ?? ""}`;
19371
19452
  }
19372
19453
  return;
19373
19454
  }
19374
- if (reply.type === "download") {
19455
+ if (reply.type === "resource-preview") {
19456
+ reply.url = connection.fileUrl(reply.url);
19457
+ if (reply.action === "inline") {
19458
+ emit(reply);
19459
+ } else {
19460
+ window.open(reply.url, "_blank", "noopener");
19461
+ }
19462
+ } else if (reply.type === "download") {
19375
19463
  const url = URL.createObjectURL(new Blob([reply.content], { type: reply.mimeType ?? "application/json" }));
19376
19464
  const link = document.createElement("a");
19377
19465
  link.href = url;
@@ -19439,6 +19527,29 @@ ${state2.user?.avatarUrl ?? ""}`;
19439
19527
  if (message.type === "ready") {
19440
19528
  return;
19441
19529
  }
19530
+ if (message.type === "resource" && message.action !== "inline" && resourceHost) {
19531
+ window.parent.postMessage({ type: "workflow-resource", version: 1, request: message }, "*");
19532
+ return;
19533
+ }
19534
+ if (message.type === "resource" && message.action !== "inline") {
19535
+ const popup = window.open("", "_blank");
19536
+ try {
19537
+ const reply = (await connection.request(message)).find((reply2) => reply2.type === "resource-preview");
19538
+ if (reply && popup) {
19539
+ popup.opener = null;
19540
+ popup.location.href = connection.fileUrl(reply.url);
19541
+ } else {
19542
+ popup?.close();
19543
+ if (reply) {
19544
+ emit({ type: "error", message: t2("Allow popups to open this resource in a new window.") });
19545
+ }
19546
+ }
19547
+ } catch (error) {
19548
+ popup?.close();
19549
+ emit({ type: "error", requestId: message.requestId, message: error.message });
19550
+ }
19551
+ return;
19552
+ }
19442
19553
  if (message.type === "trace") {
19443
19554
  if (trace.open(message)) {
19444
19555
  return;
@@ -20160,7 +20271,7 @@ ${state2.user?.avatarUrl ?? ""}`;
20160
20271
  // src/web/resource-cards.ts
20161
20272
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
20162
20273
  var string = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
20163
- function conversationResourceCards(items, projectId, conversationId) {
20274
+ function conversationResourceCards(items, projectId, conversationId, storageSource = "local") {
20164
20275
  const cards = /* @__PURE__ */ new Map();
20165
20276
  for (const [index, item] of items.entries()) {
20166
20277
  if (!["agent-part", "opencode-part"].includes(String(item.metadata?.kind)) || item.metadata?.partType !== "tool") {
@@ -20223,6 +20334,9 @@ ${state2.user?.avatarUrl ?? ""}`;
20223
20334
  if (!kind2) {
20224
20335
  continue;
20225
20336
  }
20337
+ if (kind2 === "kv" || kind2 === "knowledge") {
20338
+ target2.source = input.source ?? storageSource;
20339
+ }
20226
20340
  descriptor = { kind: kind2, target: target2, title: record(output2?.document)?.title };
20227
20341
  }
20228
20342
  const kind = descriptor.kind;
@@ -20231,7 +20345,7 @@ ${state2.user?.avatarUrl ?? ""}`;
20231
20345
  }
20232
20346
  const target = Object.fromEntries(Object.entries(record(descriptor.target) ?? {}).filter((entry) => typeof entry[1] === "string" && entry[1].length < 8192));
20233
20347
  if (target.source === "current") {
20234
- target.source = "local";
20348
+ target.source = kind === "kv" || kind === "knowledge" ? storageSource : "local";
20235
20349
  target.projectId = projectId;
20236
20350
  }
20237
20351
  if (target.source && !["local", "server"].includes(target.source)) {
@@ -20257,8 +20371,18 @@ ${state2.user?.avatarUrl ?? ""}`;
20257
20371
  }
20258
20372
  }
20259
20373
  const id = string(descriptor.cardId) ?? `${kind}:${JSON.stringify(target)}`;
20260
- const title = (string(descriptor.title) ?? target.key ?? target.documentId ?? target.filePath ?? target.url ?? target.projectId ?? kind).slice(0, 200);
20261
- cards.set(id, { id, kind, title, target, itemId: item.id ?? String(index) });
20374
+ const title = (string(descriptor.title) ?? string(record(output2?.document)?.title) ?? target.key ?? target.documentId ?? target.filePath ?? target.url ?? target.projectId ?? kind).slice(0, 200);
20375
+ const kanban = record(descriptor.kanban), values = record(kanban?.values);
20376
+ cards.set(id, {
20377
+ id,
20378
+ kind,
20379
+ title,
20380
+ target,
20381
+ itemId: item.id ?? String(index),
20382
+ ...typeof descriptor.preview === "boolean" ? { preview: descriptor.preview } : {},
20383
+ ...typeof descriptor.pinned === "boolean" ? { pinned: descriptor.pinned } : {},
20384
+ ...kind === "kanban" && ["preview", "save"].includes(String(kanban?.mode)) && values && Object.keys(values).length <= 100 && Object.values(values).every((value) => typeof value === "string") ? { kanban: { mode: kanban.mode, values } } : {}
20385
+ });
20262
20386
  }
20263
20387
  return [...cards.values()];
20264
20388
  }
@@ -20452,15 +20576,53 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
20452
20576
  this.root = root;
20453
20577
  this.options = options;
20454
20578
  root.className = "message-output";
20579
+ window.addEventListener("message", this.resourceReply);
20580
+ this.resourceTheme.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
20455
20581
  }
20456
20582
  root;
20457
20583
  options;
20458
20584
  views = /* @__PURE__ */ new Map();
20459
20585
  groups = /* @__PURE__ */ new Map();
20460
20586
  activities = /* @__PURE__ */ new Map();
20461
- update(message, projectId, conversationId, files) {
20587
+ resourceTheme = new MutationObserver(() => this.syncResourceTheme());
20588
+ syncResourceTheme() {
20589
+ for (const frame of this.root.querySelectorAll(".resource-inline > iframe")) {
20590
+ frame.contentWindow?.postMessage({ type: "workflow-host", theme: document.documentElement.dataset.theme }, location.origin);
20591
+ }
20592
+ }
20593
+ resourceReply = (event) => {
20594
+ const reply = event.data;
20595
+ if (event.source !== null || !reply?.requestId) {
20596
+ return;
20597
+ }
20598
+ const card = Array.from(this.root.querySelectorAll(".resource-card")).find((card2) => card2.dataset.requestId === reply.requestId);
20599
+ if (!card) {
20600
+ return;
20601
+ }
20602
+ const preview = card.querySelector(".resource-inline");
20603
+ if (reply.type === "error") {
20604
+ preview.textContent = reply.message;
20605
+ preview.setAttribute("role", "alert");
20606
+ }
20607
+ if (reply.type === "resource-preview" && reply.action === "inline") {
20608
+ const frame = document.createElement("iframe");
20609
+ frame.title = card.querySelector(".resource-title").textContent ?? "";
20610
+ const address = new URL(reply.url, location.href);
20611
+ address.searchParams.set("resourcePresentation", "card");
20612
+ frame.src = address.href;
20613
+ frame.sandbox.add("allow-scripts", "allow-same-origin");
20614
+ frame.addEventListener("load", () => this.syncResourceTheme());
20615
+ preview.replaceChildren(frame);
20616
+ preview.removeAttribute("role");
20617
+ }
20618
+ };
20619
+ dispose() {
20620
+ window.removeEventListener("message", this.resourceReply);
20621
+ this.resourceTheme.disconnect();
20622
+ }
20623
+ update(message, projectId, conversationId, files, storageSource = "local") {
20462
20624
  const items = message.items ?? [];
20463
- const cards = conversationResourceCards(items, projectId, conversationId);
20625
+ const cards = conversationResourceCards(items, projectId, conversationId, storageSource);
20464
20626
  const cardByItem = new Map(cards.map((card) => [card.itemId, card]));
20465
20627
  const answers = [...new Map([...message.resolvedInputs ?? [], ...message.submittingInput ? [message.submittingInput] : []].map((answer) => [answer.requestId, answer])).values()];
20466
20628
  const ordered = (message.timers ?? []).map((timer) => ({ key: `timer:${timer.timerId}`, timer }));
@@ -20480,14 +20642,18 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
20480
20642
  const blocks = [];
20481
20643
  for (const entry of ordered) {
20482
20644
  const content = entry.item?.content;
20483
- if (content?.tool === "workflow-ui_present_card" && !entry.card) {
20645
+ if (content?.tool === "workflow-ui_present_card" && content.status === "success" && !entry.card) {
20484
20646
  continue;
20485
20647
  }
20486
- if (entry.item && content?.tool !== "workflow-ui_present_card") {
20648
+ if (entry.item && (content?.tool !== "workflow-ui_present_card" || content.status !== "success")) {
20487
20649
  entry.activity = agentActivity(entry.item, message);
20488
20650
  }
20489
- if (entry.activity?.kind === "read" && !entry.card) {
20490
- entry.card = conversationResourceCards([entry.item], projectId, conversationId)[0];
20651
+ if (entry.item && content?.tool !== "workflow-ui_present_card" && !entry.card) {
20652
+ entry.card = conversationResourceCards([entry.item], projectId, conversationId, storageSource)[0];
20653
+ }
20654
+ if (entry.card && entry.card.kind !== "file") {
20655
+ blocks.push({ key: `resource:${entry.key}`, card: entry.card, item: entry.item });
20656
+ continue;
20491
20657
  }
20492
20658
  if (entry.activity) {
20493
20659
  const last = blocks.at(-1);
@@ -20514,7 +20680,7 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
20514
20680
  view.signature = signature;
20515
20681
  }
20516
20682
  } else if (!view || view.signature !== signature) {
20517
- const root = entry.answer ? this.answer(entry.answer, message.submittingInput?.requestId === entry.answer.requestId) : entry.timer ? this.timer(entry.timer) : activity ? this.activity(activity, entry.key, entry.card) : entry.card ? this.card(entry.card) : entry.item ? this.item(entry.item, files) : this.markdown(entry.text ?? "", files);
20683
+ const root = entry.answer ? this.answer(entry.answer, message.submittingInput?.requestId === entry.answer.requestId) : entry.timer ? this.timer(entry.timer) : activity ? this.activity(activity, entry.key, entry.card) : entry.card ? this.card(entry.card, entry.item) : entry.item ? this.item(entry.item, files) : this.markdown(entry.text ?? "", files);
20518
20684
  if (view?.root instanceof HTMLDetailsElement && root instanceof HTMLDetailsElement) {
20519
20685
  root.open = view.root.open;
20520
20686
  }
@@ -20848,13 +21014,67 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
20848
21014
  root.append(entry);
20849
21015
  return root;
20850
21016
  }
20851
- card(card) {
21017
+ card(card, item) {
21018
+ const root = document.createElement("section");
21019
+ root.className = "resource-card";
21020
+ root.dataset.cardId = card.id;
21021
+ const row = document.createElement("div");
21022
+ row.className = "resource-card-row";
20852
21023
  const button = document.createElement("button");
20853
- button.className = "resource-card";
20854
- button.title = card.title;
20855
- button.innerHTML = `${iconHtml(card.kind === "browser" ? "globe" : card.kind === "file" ? "file" : card.kind === "kv" ? "database" : card.kind === "knowledge" ? "book" : "folder")}<span>${escapeHtml(card.title)}</span>${iconHtml("arrow-up-right")}`;
20856
- button.addEventListener("click", () => this.options.resource(card));
20857
- return button;
21024
+ button.className = "resource-target";
21025
+ button.title = t2("Open resource");
21026
+ button.innerHTML = `${iconHtml(card.kind === "browser" ? "globe" : card.kind === "file" ? "file" : card.kind === "kv" ? "database" : card.kind === "knowledge" ? "book" : card.kind === "kanban" ? "layout" : "folder")}<span class="resource-title">${escapeHtml(card.title)}</span>`;
21027
+ button.addEventListener("click", () => this.options.resource(card, "open"));
21028
+ const preview = document.createElement("div");
21029
+ preview.className = "resource-inline";
21030
+ preview.hidden = true;
21031
+ const toggle = iconButton("eye", t2("Show in conversation"), () => {
21032
+ preview.hidden = !preview.hidden;
21033
+ toggle.setAttribute("aria-expanded", String(!preview.hidden));
21034
+ toggle.title = preview.hidden ? t2("Show in conversation") : t2("Hide preview");
21035
+ if (preview.hidden) {
21036
+ preview.replaceChildren();
21037
+ delete root.dataset.requestId;
21038
+ return;
21039
+ }
21040
+ const requestId2 = crypto.randomUUID();
21041
+ root.dataset.requestId = requestId2;
21042
+ preview.textContent = t2("Loading...");
21043
+ preview.setAttribute("role", "status");
21044
+ this.options.resource(card, "inline", requestId2);
21045
+ });
21046
+ toggle.setAttribute("aria-expanded", "false");
21047
+ toggle.dataset.action = "inline";
21048
+ const separate = iconButton("open-preview", t2("Open in new window"), () => this.options.resource(card, "window"));
21049
+ separate.dataset.action = "window";
21050
+ row.append(button, toggle, separate);
21051
+ root.append(row, preview);
21052
+ if (card.kind === "project" || card.kind === "kanban") {
21053
+ row.append(iconButton("folder-opened", t2("Open project"), () => this.options.resource(card, "project")));
21054
+ }
21055
+ if (item) {
21056
+ const details = document.createElement("div");
21057
+ details.className = "resource-call-details";
21058
+ details.hidden = true;
21059
+ const data = document.createElement("pre");
21060
+ data.textContent = JSON.stringify(item.content, null, 2);
21061
+ details.append(data);
21062
+ const inspect = iconButton("info", t2("Tool call details"), () => {
21063
+ details.hidden = !details.hidden;
21064
+ inspect.setAttribute("aria-expanded", String(!details.hidden));
21065
+ });
21066
+ inspect.setAttribute("aria-expanded", "false");
21067
+ row.append(inspect);
21068
+ root.append(details);
21069
+ }
21070
+ if (card.preview && ["project", "kanban", "browser"].includes(card.kind)) {
21071
+ queueMicrotask(() => {
21072
+ if (root.isConnected) {
21073
+ toggle.click();
21074
+ }
21075
+ });
21076
+ }
21077
+ return root;
20858
21078
  }
20859
21079
  };
20860
21080
 
@@ -21439,7 +21659,7 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
21439
21659
  plan: (text3) => dialogs.plan(text3),
21440
21660
  file: (id) => host.postMessage({ type: "open-file", id }),
21441
21661
  image: (src, title) => dialogs.image(src, title),
21442
- resource: (card) => post({ type: "resource", cardId: card.id, runId: state?.run?.id })
21662
+ resource: (card, action, requestId2) => post({ type: "resource", cardId: card.id, runId: state?.run?.id, action, requestId: requestId2 })
21443
21663
  });
21444
21664
  var parameters = () => state ? workflowParameters(state.project, entrypointId) : { fields: [] };
21445
21665
  var draft = () => drafts[entrypointId ?? ""] ??= { scopeId: "", values: {}, rawArgs: "[]", inputMode: "form", policies: {} };
@@ -21747,7 +21967,7 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
21747
21967
  submittingInput: run.submittingInput,
21748
21968
  events: run.events,
21749
21969
  timers: run.timers
21750
- }, state.project.id, run.scopeId, files);
21970
+ }, state.project.id, run.scopeId, files, state.project.projectInfo?.dataStorage?.mode === "server" ? "server" : "local");
21751
21971
  const pendingLabel = element("run-output").querySelector(".pending > span:last-child");
21752
21972
  if (pendingLabel) {
21753
21973
  pendingLabel.textContent = t2("Running...");