@workflow-code/cli 0.2.4-20261012005.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.
@@ -16172,6 +16172,8 @@
16172
16172
  "Configuration name": "\u914D\u7F6E\u540D\u79F0",
16173
16173
  "Connecting to project": "\u6B63\u5728\u8FDE\u63A5\u9879\u76EE",
16174
16174
  "Connection changed.": "\u8FDE\u63A5\u5DF2\u53D8\u66F4\u3002",
16175
+ "Copy timed out.": "\u590D\u5236\u8D85\u65F6\u3002",
16176
+ "Host clipboard is unavailable.": "\u5BBF\u4E3B\u526A\u8D34\u677F\u4E0D\u53EF\u7528\u3002",
16175
16177
  "Connection interrupted": "\u8FDE\u63A5\u5DF2\u4E2D\u65AD",
16176
16178
  "Connection interrupted.": "\u8FDE\u63A5\u5DF2\u4E2D\u65AD\u3002",
16177
16179
  "Connection not verified": "\u8FDE\u63A5\u672A\u9A8C\u8BC1",
@@ -16412,7 +16414,33 @@
16412
16414
  "Write file": "\u5199\u5165\u6587\u4EF6",
16413
16415
  Yes: "\u662F",
16414
16416
  "Zoom in": "\u653E\u5927",
16415
- "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"
16416
16444
  };
16417
16445
 
16418
16446
  // src/web/localization.ts
@@ -18875,11 +18903,14 @@ ${state2.user?.avatarUrl ?? ""}`;
18875
18903
  var ConnectionError = class extends Error {
18876
18904
  };
18877
18905
  var BrowserConnection = class {
18878
- constructor(emit) {
18906
+ constructor(emit, live = true) {
18879
18907
  this.emit = emit;
18908
+ this.live = live;
18880
18909
  if (this.token) {
18910
+ const query = new URLSearchParams(location.search);
18911
+ query.delete("session");
18881
18912
  sessionStorage.setItem("workflow-web-session", this.token);
18882
- history.replaceState(null, "", `${location.pathname}${location.search}`);
18913
+ history.replaceState(null, "", `${location.pathname}${query.size ? `?${query}` : ""}`);
18883
18914
  }
18884
18915
  queueMicrotask(() => {
18885
18916
  this.banner = document.createElement("div");
@@ -18913,6 +18944,7 @@ ${state2.user?.avatarUrl ?? ""}`;
18913
18944
  });
18914
18945
  }
18915
18946
  emit;
18947
+ live;
18916
18948
  clientId = crypto.randomUUID();
18917
18949
  source;
18918
18950
  banner;
@@ -18920,10 +18952,11 @@ ${state2.user?.avatarUrl ?? ""}`;
18920
18952
  handshake;
18921
18953
  epoch = 0;
18922
18954
  retry;
18955
+ snapshotTimer;
18923
18956
  status = "connecting";
18924
18957
  attempts = 0;
18925
18958
  closed = false;
18926
- 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");
18927
18960
  update(status) {
18928
18961
  this.status = status;
18929
18962
  this.emit({ type: "connection", status });
@@ -18946,6 +18979,7 @@ ${state2.user?.avatarUrl ?? ""}`;
18946
18979
  this.source?.close();
18947
18980
  this.source = void 0;
18948
18981
  clearTimeout(this.retry);
18982
+ clearTimeout(this.snapshotTimer);
18949
18983
  }
18950
18984
  interrupted() {
18951
18985
  this.disconnect();
@@ -18999,6 +19033,10 @@ ${state2.user?.avatarUrl ?? ""}`;
18999
19033
  if (!session.ok) {
19000
19034
  throw new Error("Cannot connect to the project.");
19001
19035
  }
19036
+ if (!this.live) {
19037
+ await this.snapshot(epoch);
19038
+ return;
19039
+ }
19002
19040
  const eventsUrl = new URL(this.fileUrl("/api/events"), location.href);
19003
19041
  eventsUrl.searchParams.set("clientId", this.clientId);
19004
19042
  const source = this.source = new EventSource(eventsUrl.href);
@@ -19035,6 +19073,39 @@ ${state2.user?.avatarUrl ?? ""}`;
19035
19073
  });
19036
19074
  return this.connecting;
19037
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
+ }
19038
19109
  async request(message) {
19039
19110
  await this.connecting;
19040
19111
  if (this.status !== "connected") {
@@ -19080,7 +19151,12 @@ ${state2.user?.avatarUrl ?? ""}`;
19080
19151
  return this.token ? { Authorization: `Bearer ${this.token}` } : {};
19081
19152
  }
19082
19153
  fileUrl(path) {
19083
- 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}`;
19084
19160
  }
19085
19161
  publicFiles(value) {
19086
19162
  if (Array.isArray(value)) {
@@ -19237,8 +19313,71 @@ ${state2.user?.avatarUrl ?? ""}`;
19237
19313
  }
19238
19314
  };
19239
19315
 
19316
+ // src/web/ui/clipboard-bridge.ts
19317
+ var ClipboardBridge = class {
19318
+ constructor(send, browserWrite, requiresHost = false) {
19319
+ this.send = send;
19320
+ this.browserWrite = browserWrite;
19321
+ this.requiresHost = requiresHost;
19322
+ }
19323
+ send;
19324
+ browserWrite;
19325
+ requiresHost;
19326
+ enabled = false;
19327
+ pending = /* @__PURE__ */ new Map();
19328
+ enable(version) {
19329
+ this.enabled = version === 1;
19330
+ }
19331
+ async writeText(text3) {
19332
+ if (!this.enabled) {
19333
+ if (this.requiresHost) {
19334
+ throw new Error(t2("Host clipboard is unavailable."));
19335
+ }
19336
+ return this.browserWrite(text3);
19337
+ }
19338
+ const requestId2 = crypto.randomUUID();
19339
+ return new Promise((resolve2, reject) => {
19340
+ const timer = setTimeout(() => {
19341
+ this.pending.delete(requestId2);
19342
+ reject(new Error(t2("Copy timed out.")));
19343
+ }, 1e4);
19344
+ this.pending.set(requestId2, { resolve: resolve2, reject, timer });
19345
+ try {
19346
+ this.send({ type: "workflow-clipboard", version: 1, command: "writeText", requestId: requestId2, text: text3 });
19347
+ } catch (error) {
19348
+ clearTimeout(timer);
19349
+ this.pending.delete(requestId2);
19350
+ reject(error);
19351
+ }
19352
+ });
19353
+ }
19354
+ receive(reply) {
19355
+ if (reply?.version !== 1 || typeof reply.requestId !== "string" || typeof reply.ok !== "boolean" || reply.error !== void 0 && typeof reply.error !== "string") {
19356
+ return;
19357
+ }
19358
+ const pending = this.pending.get(reply.requestId);
19359
+ if (!pending) {
19360
+ return;
19361
+ }
19362
+ this.pending.delete(reply.requestId);
19363
+ clearTimeout(pending.timer);
19364
+ if (reply.ok) {
19365
+ pending.resolve();
19366
+ } else {
19367
+ pending.reject(new Error(reply.error || t2("Host clipboard is unavailable.")));
19368
+ }
19369
+ }
19370
+ dispose() {
19371
+ for (const pending of this.pending.values()) {
19372
+ clearTimeout(pending.timer);
19373
+ pending.reject(new Error(t2("Connection changed.")));
19374
+ }
19375
+ this.pending.clear();
19376
+ }
19377
+ };
19378
+
19240
19379
  // src/web/ui/host.ts
19241
- function acquireWorkflowHost() {
19380
+ function acquireWorkflowHost(options = {}) {
19242
19381
  const embedded = globalThis.acquireVsCodeApi;
19243
19382
  if (embedded) {
19244
19383
  return embedded();
@@ -19258,13 +19397,20 @@ ${state2.user?.avatarUrl ?? ""}`;
19258
19397
  window.parent.postMessage(message, "*");
19259
19398
  }
19260
19399
  });
19400
+ let resourceHost = false;
19401
+ const clipboard = new ClipboardBridge(
19402
+ (message) => window.parent.postMessage(message, "*"),
19403
+ (text3) => navigator.clipboard.writeText(text3),
19404
+ window.parent !== window && document.body.dataset.accountHost === "true"
19405
+ );
19406
+ addEventListener("pagehide", () => clipboard.dispose());
19261
19407
  const connection = new BrowserConnection((message) => {
19262
19408
  if (message.type === "state" && message.account) {
19263
19409
  accountPanel.update(message.account);
19264
19410
  }
19265
19411
  trace.receive(message);
19266
19412
  emit(message);
19267
- });
19413
+ }, options.live);
19268
19414
  const request = async (message) => {
19269
19415
  for (const entry of await connection.request(message)) {
19270
19416
  handleReply(entry);
@@ -19280,6 +19426,16 @@ ${state2.user?.avatarUrl ?? ""}`;
19280
19426
  if (event.data.traceVersion !== void 0) {
19281
19427
  trace.enable(event.data.traceVersion);
19282
19428
  }
19429
+ if (event.data.clipboardVersion !== void 0) {
19430
+ clipboard.enable(event.data.clipboardVersion);
19431
+ }
19432
+ if (event.data.resourceVersion === 1) {
19433
+ resourceHost = true;
19434
+ }
19435
+ if (event.data.resourceError) {
19436
+ emit(event.data.resourceError);
19437
+ }
19438
+ clipboard.receive(event.data.clipboard);
19283
19439
  const traceCommand = trace.command(event.data.traceCommand);
19284
19440
  if (traceCommand) {
19285
19441
  void request(traceCommand).catch((error) => trace.receive({ type: "trace-run", runId: traceCommand.id, error: error.message }));
@@ -19296,7 +19452,14 @@ ${state2.user?.avatarUrl ?? ""}`;
19296
19452
  }
19297
19453
  return;
19298
19454
  }
19299
- 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") {
19300
19463
  const url = URL.createObjectURL(new Blob([reply.content], { type: reply.mimeType ?? "application/json" }));
19301
19464
  const link = document.createElement("a");
19302
19465
  link.href = url;
@@ -19364,6 +19527,29 @@ ${state2.user?.avatarUrl ?? ""}`;
19364
19527
  if (message.type === "ready") {
19365
19528
  return;
19366
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
+ }
19367
19553
  if (message.type === "trace") {
19368
19554
  if (trace.open(message)) {
19369
19555
  return;
@@ -19373,8 +19559,13 @@ ${state2.user?.avatarUrl ?? ""}`;
19373
19559
  }
19374
19560
  }
19375
19561
  if (message.type === "copy") {
19376
- void navigator.clipboard.writeText(message.text).catch((error) => emit({ type: "error", message: error.message }));
19377
- return;
19562
+ try {
19563
+ await clipboard.writeText(message.text);
19564
+ } catch (error) {
19565
+ emit({ type: "error", message: error.message });
19566
+ return false;
19567
+ }
19568
+ return true;
19378
19569
  }
19379
19570
  if (message.type === "attach") {
19380
19571
  chooseFiles(message);
@@ -20080,7 +20271,7 @@ ${state2.user?.avatarUrl ?? ""}`;
20080
20271
  // src/web/resource-cards.ts
20081
20272
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
20082
20273
  var string = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
20083
- function conversationResourceCards(items, projectId, conversationId) {
20274
+ function conversationResourceCards(items, projectId, conversationId, storageSource = "local") {
20084
20275
  const cards = /* @__PURE__ */ new Map();
20085
20276
  for (const [index, item] of items.entries()) {
20086
20277
  if (!["agent-part", "opencode-part"].includes(String(item.metadata?.kind)) || item.metadata?.partType !== "tool") {
@@ -20143,6 +20334,9 @@ ${state2.user?.avatarUrl ?? ""}`;
20143
20334
  if (!kind2) {
20144
20335
  continue;
20145
20336
  }
20337
+ if (kind2 === "kv" || kind2 === "knowledge") {
20338
+ target2.source = input.source ?? storageSource;
20339
+ }
20146
20340
  descriptor = { kind: kind2, target: target2, title: record(output2?.document)?.title };
20147
20341
  }
20148
20342
  const kind = descriptor.kind;
@@ -20151,7 +20345,7 @@ ${state2.user?.avatarUrl ?? ""}`;
20151
20345
  }
20152
20346
  const target = Object.fromEntries(Object.entries(record(descriptor.target) ?? {}).filter((entry) => typeof entry[1] === "string" && entry[1].length < 8192));
20153
20347
  if (target.source === "current") {
20154
- target.source = "local";
20348
+ target.source = kind === "kv" || kind === "knowledge" ? storageSource : "local";
20155
20349
  target.projectId = projectId;
20156
20350
  }
20157
20351
  if (target.source && !["local", "server"].includes(target.source)) {
@@ -20177,8 +20371,18 @@ ${state2.user?.avatarUrl ?? ""}`;
20177
20371
  }
20178
20372
  }
20179
20373
  const id = string(descriptor.cardId) ?? `${kind}:${JSON.stringify(target)}`;
20180
- const title = (string(descriptor.title) ?? target.key ?? target.documentId ?? target.filePath ?? target.url ?? target.projectId ?? kind).slice(0, 200);
20181
- 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
+ });
20182
20386
  }
20183
20387
  return [...cards.values()];
20184
20388
  }
@@ -20372,15 +20576,53 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
20372
20576
  this.root = root;
20373
20577
  this.options = options;
20374
20578
  root.className = "message-output";
20579
+ window.addEventListener("message", this.resourceReply);
20580
+ this.resourceTheme.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
20375
20581
  }
20376
20582
  root;
20377
20583
  options;
20378
20584
  views = /* @__PURE__ */ new Map();
20379
20585
  groups = /* @__PURE__ */ new Map();
20380
20586
  activities = /* @__PURE__ */ new Map();
20381
- 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") {
20382
20624
  const items = message.items ?? [];
20383
- const cards = conversationResourceCards(items, projectId, conversationId);
20625
+ const cards = conversationResourceCards(items, projectId, conversationId, storageSource);
20384
20626
  const cardByItem = new Map(cards.map((card) => [card.itemId, card]));
20385
20627
  const answers = [...new Map([...message.resolvedInputs ?? [], ...message.submittingInput ? [message.submittingInput] : []].map((answer) => [answer.requestId, answer])).values()];
20386
20628
  const ordered = (message.timers ?? []).map((timer) => ({ key: `timer:${timer.timerId}`, timer }));
@@ -20400,14 +20642,18 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
20400
20642
  const blocks = [];
20401
20643
  for (const entry of ordered) {
20402
20644
  const content = entry.item?.content;
20403
- if (content?.tool === "workflow-ui_present_card" && !entry.card) {
20645
+ if (content?.tool === "workflow-ui_present_card" && content.status === "success" && !entry.card) {
20404
20646
  continue;
20405
20647
  }
20406
- if (entry.item && content?.tool !== "workflow-ui_present_card") {
20648
+ if (entry.item && (content?.tool !== "workflow-ui_present_card" || content.status !== "success")) {
20407
20649
  entry.activity = agentActivity(entry.item, message);
20408
20650
  }
20409
- if (entry.activity?.kind === "read" && !entry.card) {
20410
- 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;
20411
20657
  }
20412
20658
  if (entry.activity) {
20413
20659
  const last = blocks.at(-1);
@@ -20434,7 +20680,7 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
20434
20680
  view.signature = signature;
20435
20681
  }
20436
20682
  } else if (!view || view.signature !== signature) {
20437
- 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);
20438
20684
  if (view?.root instanceof HTMLDetailsElement && root instanceof HTMLDetailsElement) {
20439
20685
  root.open = view.root.open;
20440
20686
  }
@@ -20768,13 +21014,67 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
20768
21014
  root.append(entry);
20769
21015
  return root;
20770
21016
  }
20771
- 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";
20772
21023
  const button = document.createElement("button");
20773
- button.className = "resource-card";
20774
- button.title = card.title;
20775
- 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")}`;
20776
- button.addEventListener("click", () => this.options.resource(card));
20777
- 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;
20778
21078
  }
20779
21079
  };
20780
21080
 
@@ -21359,7 +21659,7 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
21359
21659
  plan: (text3) => dialogs.plan(text3),
21360
21660
  file: (id) => host.postMessage({ type: "open-file", id }),
21361
21661
  image: (src, title) => dialogs.image(src, title),
21362
- 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 })
21363
21663
  });
21364
21664
  var parameters = () => state ? workflowParameters(state.project, entrypointId) : { fields: [] };
21365
21665
  var draft = () => drafts[entrypointId ?? ""] ??= { scopeId: "", values: {}, rawArgs: "[]", inputMode: "form", policies: {} };
@@ -21667,7 +21967,7 @@ ${JSON.stringify(output2.details, null, 2)}` : "");
21667
21967
  submittingInput: run.submittingInput,
21668
21968
  events: run.events,
21669
21969
  timers: run.timers
21670
- }, state.project.id, run.scopeId, files);
21970
+ }, state.project.id, run.scopeId, files, state.project.projectInfo?.dataStorage?.mode === "server" ? "server" : "local");
21671
21971
  const pendingLabel = element("run-output").querySelector(".pending > span:last-child");
21672
21972
  if (pendingLabel) {
21673
21973
  pendingLabel.textContent = t2("Running...");