@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.
@@ -214,14 +214,14 @@ try {
214
214
  const rootDir = request.kind === 'knowledge' ? path.join(dataDir, 'values') : path.join(dataDir, 'kv');
215
215
  const store = createLocalWorkflowKVStore({ rootDir, storageKind: request.kind === 'knowledge' ? 'persistent_value' : 'kv' });
216
216
  const context = request.kind === 'knowledge'
217
- ? createWorkflowPersistentValueContext({ store, projectId: request.project.id, conversationId: request.conversationId })
218
- : createWorkflowKVContext({ store, projectId: request.project.id, conversationId: request.conversationId });
217
+ ? createWorkflowPersistentValueContext({ store, projectId: request.project.id, conversationId: target.conversationId ?? request.conversationId })
218
+ : createWorkflowKVContext({ store, projectId: request.project.id, conversationId: target.conversationId ?? request.conversationId });
219
219
  if (request.kind === 'knowledge') {
220
220
  send('result', target.documentId ? await getKnowledgeDocument({ persistentValue: context }, target.documentId) : await listKnowledgeDocuments({ persistentValue: context }));
221
221
  } else if (target.key) {
222
222
  send('result', await (target.scope === 'conversation' ? context.conversation : context).getValue(target.key) ?? null);
223
223
  } else {
224
- send('result', await listLocalWorkflowKVEntries({ rootDir, storageKind: 'kv', projectId: request.project.id, scope: target.scope === 'conversation' ? 'conversation' : 'project', conversationId: target.scope === 'conversation' ? request.conversationId : undefined, pageSize: 100 }));
224
+ send('result', await listLocalWorkflowKVEntries({ rootDir, storageKind: 'kv', projectId: request.project.id, scope: target.scope === 'conversation' ? 'conversation' : 'project', conversationId: target.scope === 'conversation' ? target.conversationId ?? request.conversationId : undefined, pageSize: 100 }));
225
225
  }
226
226
  } else if (request.method === 'delete-conversation') {
227
227
  for (const [storageKind, rootDir] of [['kv', path.join(dataDir, 'kv')], ['persistent_value', path.join(dataDir, 'values')]]) {
@@ -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;
@@ -12885,7 +12885,33 @@
12885
12885
  "Write file": "\u5199\u5165\u6587\u4EF6",
12886
12886
  Yes: "\u662F",
12887
12887
  "Zoom in": "\u653E\u5927",
12888
- "Zoom out": "\u7F29\u5C0F"
12888
+ "Zoom out": "\u7F29\u5C0F",
12889
+ "Open resource": "\u6253\u5F00\u8D44\u6E90",
12890
+ "Show in conversation": "\u5728\u5BF9\u8BDD\u4E2D\u5C55\u793A",
12891
+ "Hide preview": "\u6536\u8D77\u9884\u89C8",
12892
+ "Open in new window": "\u5728\u65B0\u7A97\u53E3\u4E2D\u6253\u5F00",
12893
+ "Tool call details": "\u5DE5\u5177\u8C03\u7528\u8BE6\u60C5",
12894
+ "Please sign in to preview this server resource.": "\u8BF7\u767B\u5F55\u540E\u9884\u89C8\u670D\u52A1\u5668\u8D44\u6E90\u3002",
12895
+ "You do not have access to this resource.": "\u4F60\u6CA1\u6709\u6B64\u8D44\u6E90\u7684\u8BBF\u95EE\u6743\u9650\u3002",
12896
+ "The resource project is unavailable.": "\u8D44\u6E90\u6240\u5C5E\u9879\u76EE\u4E0D\u53EF\u7528\u3002",
12897
+ "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",
12898
+ "The project does not allow this storage location.": "\u9879\u76EE\u4E0D\u5141\u8BB8\u4F7F\u7528\u6B64\u5B58\u50A8\u4F4D\u7F6E\u3002",
12899
+ "This resource no longer exists.": "\u6B64\u8D44\u6E90\u5DF2\u4E0D\u5B58\u5728\u3002",
12900
+ "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",
12901
+ "File is outside this project.": "\u6587\u4EF6\u4E0D\u5728\u6B64\u9879\u76EE\u4E2D\u3002",
12902
+ "File preview exceeds 2 MiB.": "\u6587\u4EF6\u9884\u89C8\u5927\u5C0F\u8D85\u8FC7 2 MiB\u3002",
12903
+ "This project does not provide a Kanban preview.": "\u6B64\u9879\u76EE\u4E0D\u63D0\u4F9B\u770B\u677F\u9884\u89C8\u3002",
12904
+ "Invalid resource preview URL.": "\u8D44\u6E90\u9884\u89C8\u5730\u5740\u65E0\u6548\u3002",
12905
+ "This preview configuration no longer exists.": "\u6B64\u9884\u89C8\u914D\u7F6E\u5DF2\u4E0D\u5B58\u5728\u3002",
12906
+ "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",
12907
+ "This resource preview is read-only.": "\u6B64\u8D44\u6E90\u9884\u89C8\u4E3A\u53EA\u8BFB\u3002",
12908
+ "Invalid resource action.": "\u8D44\u6E90\u64CD\u4F5C\u65E0\u6548\u3002",
12909
+ "Run no longer exists.": "\u6B64\u8FD0\u884C\u5DF2\u4E0D\u5B58\u5728\u3002",
12910
+ "Conversation changed.": "\u4F1A\u8BDD\u5DF2\u53D8\u5316\u3002",
12911
+ "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",
12912
+ "Open in browser": "\u5728\u6D4F\u89C8\u5668\u4E2D\u6253\u5F00",
12913
+ "No resources": "\u6682\u65E0\u8D44\u6E90",
12914
+ "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"
12889
12915
  };
12890
12916
 
12891
12917
  // src/web/localization.ts
@@ -16140,7 +16166,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
16140
16166
  // src/web/resource-cards.ts
16141
16167
  var record = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
16142
16168
  var string = (value) => typeof value === "string" && value.trim() ? value.trim() : void 0;
16143
- function conversationResourceCards(items, projectId, conversationId) {
16169
+ function conversationResourceCards(items, projectId, conversationId, storageSource = "local") {
16144
16170
  const cards = /* @__PURE__ */ new Map();
16145
16171
  for (const [index, item] of items.entries()) {
16146
16172
  if (!["agent-part", "opencode-part"].includes(String(item.metadata?.kind)) || item.metadata?.partType !== "tool") {
@@ -16203,6 +16229,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
16203
16229
  if (!kind2) {
16204
16230
  continue;
16205
16231
  }
16232
+ if (kind2 === "kv" || kind2 === "knowledge") {
16233
+ target2.source = input.source ?? storageSource;
16234
+ }
16206
16235
  descriptor = { kind: kind2, target: target2, title: record(output?.document)?.title };
16207
16236
  }
16208
16237
  const kind = descriptor.kind;
@@ -16211,7 +16240,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
16211
16240
  }
16212
16241
  const target = Object.fromEntries(Object.entries(record(descriptor.target) ?? {}).filter((entry) => typeof entry[1] === "string" && entry[1].length < 8192));
16213
16242
  if (target.source === "current") {
16214
- target.source = "local";
16243
+ target.source = kind === "kv" || kind === "knowledge" ? storageSource : "local";
16215
16244
  target.projectId = projectId;
16216
16245
  }
16217
16246
  if (target.source && !["local", "server"].includes(target.source)) {
@@ -16237,8 +16266,18 @@ Please report this to https://github.com/markedjs/marked.`, e) {
16237
16266
  }
16238
16267
  }
16239
16268
  const id = string(descriptor.cardId) ?? `${kind}:${JSON.stringify(target)}`;
16240
- const title = (string(descriptor.title) ?? target.key ?? target.documentId ?? target.filePath ?? target.url ?? target.projectId ?? kind).slice(0, 200);
16241
- cards.set(id, { id, kind, title, target, itemId: item.id ?? String(index) });
16269
+ const title = (string(descriptor.title) ?? string(record(output?.document)?.title) ?? target.key ?? target.documentId ?? target.filePath ?? target.url ?? target.projectId ?? kind).slice(0, 200);
16270
+ const kanban = record(descriptor.kanban), values = record(kanban?.values);
16271
+ cards.set(id, {
16272
+ id,
16273
+ kind,
16274
+ title,
16275
+ target,
16276
+ itemId: item.id ?? String(index),
16277
+ ...typeof descriptor.preview === "boolean" ? { preview: descriptor.preview } : {},
16278
+ ...typeof descriptor.pinned === "boolean" ? { pinned: descriptor.pinned } : {},
16279
+ ...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 } } : {}
16280
+ });
16242
16281
  }
16243
16282
  return [...cards.values()];
16244
16283
  }
@@ -16432,15 +16471,53 @@ ${JSON.stringify(output.details, null, 2)}` : "");
16432
16471
  this.root = root;
16433
16472
  this.options = options;
16434
16473
  root.className = "message-output";
16474
+ window.addEventListener("message", this.resourceReply);
16475
+ this.resourceTheme.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
16435
16476
  }
16436
16477
  root;
16437
16478
  options;
16438
16479
  views = /* @__PURE__ */ new Map();
16439
16480
  groups = /* @__PURE__ */ new Map();
16440
16481
  activities = /* @__PURE__ */ new Map();
16441
- update(message, projectId, conversationId, files) {
16482
+ resourceTheme = new MutationObserver(() => this.syncResourceTheme());
16483
+ syncResourceTheme() {
16484
+ for (const frame of this.root.querySelectorAll(".resource-inline > iframe")) {
16485
+ frame.contentWindow?.postMessage({ type: "workflow-host", theme: document.documentElement.dataset.theme }, location.origin);
16486
+ }
16487
+ }
16488
+ resourceReply = (event) => {
16489
+ const reply = event.data;
16490
+ if (event.source !== null || !reply?.requestId) {
16491
+ return;
16492
+ }
16493
+ const card = Array.from(this.root.querySelectorAll(".resource-card")).find((card2) => card2.dataset.requestId === reply.requestId);
16494
+ if (!card) {
16495
+ return;
16496
+ }
16497
+ const preview = card.querySelector(".resource-inline");
16498
+ if (reply.type === "error") {
16499
+ preview.textContent = reply.message;
16500
+ preview.setAttribute("role", "alert");
16501
+ }
16502
+ if (reply.type === "resource-preview" && reply.action === "inline") {
16503
+ const frame = document.createElement("iframe");
16504
+ frame.title = card.querySelector(".resource-title").textContent ?? "";
16505
+ const address = new URL(reply.url, location.href);
16506
+ address.searchParams.set("resourcePresentation", "card");
16507
+ frame.src = address.href;
16508
+ frame.sandbox.add("allow-scripts", "allow-same-origin");
16509
+ frame.addEventListener("load", () => this.syncResourceTheme());
16510
+ preview.replaceChildren(frame);
16511
+ preview.removeAttribute("role");
16512
+ }
16513
+ };
16514
+ dispose() {
16515
+ window.removeEventListener("message", this.resourceReply);
16516
+ this.resourceTheme.disconnect();
16517
+ }
16518
+ update(message, projectId, conversationId, files, storageSource = "local") {
16442
16519
  const items = message.items ?? [];
16443
- const cards = conversationResourceCards(items, projectId, conversationId);
16520
+ const cards = conversationResourceCards(items, projectId, conversationId, storageSource);
16444
16521
  const cardByItem = new Map(cards.map((card) => [card.itemId, card]));
16445
16522
  const answers = [...new Map([...message.resolvedInputs ?? [], ...message.submittingInput ? [message.submittingInput] : []].map((answer) => [answer.requestId, answer])).values()];
16446
16523
  const ordered = (message.timers ?? []).map((timer) => ({ key: `timer:${timer.timerId}`, timer }));
@@ -16460,14 +16537,18 @@ ${JSON.stringify(output.details, null, 2)}` : "");
16460
16537
  const blocks = [];
16461
16538
  for (const entry of ordered) {
16462
16539
  const content = entry.item?.content;
16463
- if (content?.tool === "workflow-ui_present_card" && !entry.card) {
16540
+ if (content?.tool === "workflow-ui_present_card" && content.status === "success" && !entry.card) {
16464
16541
  continue;
16465
16542
  }
16466
- if (entry.item && content?.tool !== "workflow-ui_present_card") {
16543
+ if (entry.item && (content?.tool !== "workflow-ui_present_card" || content.status !== "success")) {
16467
16544
  entry.activity = agentActivity(entry.item, message);
16468
16545
  }
16469
- if (entry.activity?.kind === "read" && !entry.card) {
16470
- entry.card = conversationResourceCards([entry.item], projectId, conversationId)[0];
16546
+ if (entry.item && content?.tool !== "workflow-ui_present_card" && !entry.card) {
16547
+ entry.card = conversationResourceCards([entry.item], projectId, conversationId, storageSource)[0];
16548
+ }
16549
+ if (entry.card && entry.card.kind !== "file") {
16550
+ blocks.push({ key: `resource:${entry.key}`, card: entry.card, item: entry.item });
16551
+ continue;
16471
16552
  }
16472
16553
  if (entry.activity) {
16473
16554
  const last = blocks.at(-1);
@@ -16494,7 +16575,7 @@ ${JSON.stringify(output.details, null, 2)}` : "");
16494
16575
  view.signature = signature;
16495
16576
  }
16496
16577
  } else if (!view || view.signature !== signature) {
16497
- 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);
16578
+ 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);
16498
16579
  if (view?.root instanceof HTMLDetailsElement && root instanceof HTMLDetailsElement) {
16499
16580
  root.open = view.root.open;
16500
16581
  }
@@ -16828,13 +16909,67 @@ ${JSON.stringify(output.details, null, 2)}` : "");
16828
16909
  root.append(entry);
16829
16910
  return root;
16830
16911
  }
16831
- card(card) {
16912
+ card(card, item) {
16913
+ const root = document.createElement("section");
16914
+ root.className = "resource-card";
16915
+ root.dataset.cardId = card.id;
16916
+ const row = document.createElement("div");
16917
+ row.className = "resource-card-row";
16832
16918
  const button = document.createElement("button");
16833
- button.className = "resource-card";
16834
- button.title = card.title;
16835
- 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")}`;
16836
- button.addEventListener("click", () => this.options.resource(card));
16837
- return button;
16919
+ button.className = "resource-target";
16920
+ button.title = t2("Open resource");
16921
+ 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>`;
16922
+ button.addEventListener("click", () => this.options.resource(card, "open"));
16923
+ const preview = document.createElement("div");
16924
+ preview.className = "resource-inline";
16925
+ preview.hidden = true;
16926
+ const toggle = iconButton("eye", t2("Show in conversation"), () => {
16927
+ preview.hidden = !preview.hidden;
16928
+ toggle.setAttribute("aria-expanded", String(!preview.hidden));
16929
+ toggle.title = preview.hidden ? t2("Show in conversation") : t2("Hide preview");
16930
+ if (preview.hidden) {
16931
+ preview.replaceChildren();
16932
+ delete root.dataset.requestId;
16933
+ return;
16934
+ }
16935
+ const requestId = crypto.randomUUID();
16936
+ root.dataset.requestId = requestId;
16937
+ preview.textContent = t2("Loading...");
16938
+ preview.setAttribute("role", "status");
16939
+ this.options.resource(card, "inline", requestId);
16940
+ });
16941
+ toggle.setAttribute("aria-expanded", "false");
16942
+ toggle.dataset.action = "inline";
16943
+ const separate = iconButton("open-preview", t2("Open in new window"), () => this.options.resource(card, "window"));
16944
+ separate.dataset.action = "window";
16945
+ row.append(button, toggle, separate);
16946
+ root.append(row, preview);
16947
+ if (card.kind === "project" || card.kind === "kanban") {
16948
+ row.append(iconButton("folder-opened", t2("Open project"), () => this.options.resource(card, "project")));
16949
+ }
16950
+ if (item) {
16951
+ const details = document.createElement("div");
16952
+ details.className = "resource-call-details";
16953
+ details.hidden = true;
16954
+ const data = document.createElement("pre");
16955
+ data.textContent = JSON.stringify(item.content, null, 2);
16956
+ details.append(data);
16957
+ const inspect = iconButton("info", t2("Tool call details"), () => {
16958
+ details.hidden = !details.hidden;
16959
+ inspect.setAttribute("aria-expanded", String(!details.hidden));
16960
+ });
16961
+ inspect.setAttribute("aria-expanded", "false");
16962
+ row.append(inspect);
16963
+ root.append(details);
16964
+ }
16965
+ if (card.preview && ["project", "kanban", "browser"].includes(card.kind)) {
16966
+ queueMicrotask(() => {
16967
+ if (root.isConnected) {
16968
+ toggle.click();
16969
+ }
16970
+ });
16971
+ }
16972
+ return root;
16838
16973
  }
16839
16974
  };
16840
16975
 
@@ -17026,11 +17161,14 @@ ${state.user?.avatarUrl ?? ""}`;
17026
17161
  var ConnectionError = class extends Error {
17027
17162
  };
17028
17163
  var BrowserConnection = class {
17029
- constructor(emit) {
17164
+ constructor(emit, live = true) {
17030
17165
  this.emit = emit;
17166
+ this.live = live;
17031
17167
  if (this.token) {
17168
+ const query = new URLSearchParams(location.search);
17169
+ query.delete("session");
17032
17170
  sessionStorage.setItem("workflow-web-session", this.token);
17033
- history.replaceState(null, "", `${location.pathname}${location.search}`);
17171
+ history.replaceState(null, "", `${location.pathname}${query.size ? `?${query}` : ""}`);
17034
17172
  }
17035
17173
  queueMicrotask(() => {
17036
17174
  this.banner = document.createElement("div");
@@ -17064,6 +17202,7 @@ ${state.user?.avatarUrl ?? ""}`;
17064
17202
  });
17065
17203
  }
17066
17204
  emit;
17205
+ live;
17067
17206
  clientId = crypto.randomUUID();
17068
17207
  source;
17069
17208
  banner;
@@ -17071,10 +17210,11 @@ ${state.user?.avatarUrl ?? ""}`;
17071
17210
  handshake;
17072
17211
  epoch = 0;
17073
17212
  retry;
17213
+ snapshotTimer;
17074
17214
  status = "connecting";
17075
17215
  attempts = 0;
17076
17216
  closed = false;
17077
- token = new URLSearchParams(location.hash.slice(1)).get("session") ?? sessionStorage.getItem("workflow-web-session");
17217
+ token = new URLSearchParams(location.hash.slice(1)).get("session") ?? new URLSearchParams(location.search).get("session") ?? sessionStorage.getItem("workflow-web-session");
17078
17218
  update(status) {
17079
17219
  this.status = status;
17080
17220
  this.emit({ type: "connection", status });
@@ -17097,6 +17237,7 @@ ${state.user?.avatarUrl ?? ""}`;
17097
17237
  this.source?.close();
17098
17238
  this.source = void 0;
17099
17239
  clearTimeout(this.retry);
17240
+ clearTimeout(this.snapshotTimer);
17100
17241
  }
17101
17242
  interrupted() {
17102
17243
  this.disconnect();
@@ -17150,6 +17291,10 @@ ${state.user?.avatarUrl ?? ""}`;
17150
17291
  if (!session.ok) {
17151
17292
  throw new Error("Cannot connect to the project.");
17152
17293
  }
17294
+ if (!this.live) {
17295
+ await this.snapshot(epoch);
17296
+ return;
17297
+ }
17153
17298
  const eventsUrl = new URL(this.fileUrl("/api/events"), location.href);
17154
17299
  eventsUrl.searchParams.set("clientId", this.clientId);
17155
17300
  const source = this.source = new EventSource(eventsUrl.href);
@@ -17186,6 +17331,39 @@ ${state.user?.avatarUrl ?? ""}`;
17186
17331
  });
17187
17332
  return this.connecting;
17188
17333
  }
17334
+ async snapshot(epoch) {
17335
+ if (epoch !== this.epoch || this.closed) {
17336
+ return;
17337
+ }
17338
+ const response = await fetch("/api/resource-state", { headers: this.headers(), signal: AbortSignal.timeout(1e4) });
17339
+ if (epoch !== this.epoch || this.closed) {
17340
+ return;
17341
+ }
17342
+ if (response.status === 401) {
17343
+ this.disconnect();
17344
+ this.update("expired");
17345
+ return;
17346
+ }
17347
+ if (!response.ok) {
17348
+ throw new Error("Cannot connect to the project.");
17349
+ }
17350
+ const state = await response.json();
17351
+ if (epoch !== this.epoch || this.closed) {
17352
+ return;
17353
+ }
17354
+ if (this.status !== "connected") {
17355
+ this.attempts = 0;
17356
+ this.update("connected");
17357
+ }
17358
+ this.emit(state);
17359
+ this.snapshotTimer = setTimeout(() => {
17360
+ void this.snapshot(epoch).catch(() => {
17361
+ if (epoch === this.epoch && !this.closed) {
17362
+ this.interrupted();
17363
+ }
17364
+ });
17365
+ }, 5e3);
17366
+ }
17189
17367
  async request(message) {
17190
17368
  await this.connecting;
17191
17369
  if (this.status !== "connected") {
@@ -17231,7 +17409,12 @@ ${state.user?.avatarUrl ?? ""}`;
17231
17409
  return this.token ? { Authorization: `Bearer ${this.token}` } : {};
17232
17410
  }
17233
17411
  fileUrl(path) {
17234
- return this.token ? `${path}?session=${encodeURIComponent(this.token)}` : path;
17412
+ if (!this.token) {
17413
+ return path;
17414
+ }
17415
+ const url = new URL(path, location.href);
17416
+ url.searchParams.set("session", this.token);
17417
+ return `${url.pathname}${url.search}${url.hash}`;
17235
17418
  }
17236
17419
  publicFiles(value) {
17237
17420
  if (Array.isArray(value)) {
@@ -17452,7 +17635,7 @@ ${state.user?.avatarUrl ?? ""}`;
17452
17635
  };
17453
17636
 
17454
17637
  // src/web/ui/host.ts
17455
- function acquireWorkflowHost() {
17638
+ function acquireWorkflowHost(options = {}) {
17456
17639
  const embedded = globalThis.acquireVsCodeApi;
17457
17640
  if (embedded) {
17458
17641
  return embedded();
@@ -17472,6 +17655,7 @@ ${state.user?.avatarUrl ?? ""}`;
17472
17655
  window.parent.postMessage(message, "*");
17473
17656
  }
17474
17657
  });
17658
+ let resourceHost = false;
17475
17659
  const clipboard = new ClipboardBridge(
17476
17660
  (message) => window.parent.postMessage(message, "*"),
17477
17661
  (text3) => navigator.clipboard.writeText(text3),
@@ -17484,7 +17668,7 @@ ${state.user?.avatarUrl ?? ""}`;
17484
17668
  }
17485
17669
  trace.receive(message);
17486
17670
  emit(message);
17487
- });
17671
+ }, options.live);
17488
17672
  const request = async (message) => {
17489
17673
  for (const entry of await connection.request(message)) {
17490
17674
  handleReply(entry);
@@ -17503,6 +17687,12 @@ ${state.user?.avatarUrl ?? ""}`;
17503
17687
  if (event.data.clipboardVersion !== void 0) {
17504
17688
  clipboard.enable(event.data.clipboardVersion);
17505
17689
  }
17690
+ if (event.data.resourceVersion === 1) {
17691
+ resourceHost = true;
17692
+ }
17693
+ if (event.data.resourceError) {
17694
+ emit(event.data.resourceError);
17695
+ }
17506
17696
  clipboard.receive(event.data.clipboard);
17507
17697
  const traceCommand = trace.command(event.data.traceCommand);
17508
17698
  if (traceCommand) {
@@ -17520,7 +17710,14 @@ ${state.user?.avatarUrl ?? ""}`;
17520
17710
  }
17521
17711
  return;
17522
17712
  }
17523
- if (reply.type === "download") {
17713
+ if (reply.type === "resource-preview") {
17714
+ reply.url = connection.fileUrl(reply.url);
17715
+ if (reply.action === "inline") {
17716
+ emit(reply);
17717
+ } else {
17718
+ window.open(reply.url, "_blank", "noopener");
17719
+ }
17720
+ } else if (reply.type === "download") {
17524
17721
  const url = URL.createObjectURL(new Blob([reply.content], { type: reply.mimeType ?? "application/json" }));
17525
17722
  const link = document.createElement("a");
17526
17723
  link.href = url;
@@ -17588,6 +17785,29 @@ ${state.user?.avatarUrl ?? ""}`;
17588
17785
  if (message.type === "ready") {
17589
17786
  return;
17590
17787
  }
17788
+ if (message.type === "resource" && message.action !== "inline" && resourceHost) {
17789
+ window.parent.postMessage({ type: "workflow-resource", version: 1, request: message }, "*");
17790
+ return;
17791
+ }
17792
+ if (message.type === "resource" && message.action !== "inline") {
17793
+ const popup = window.open("", "_blank");
17794
+ try {
17795
+ const reply = (await connection.request(message)).find((reply2) => reply2.type === "resource-preview");
17796
+ if (reply && popup) {
17797
+ popup.opener = null;
17798
+ popup.location.href = connection.fileUrl(reply.url);
17799
+ } else {
17800
+ popup?.close();
17801
+ if (reply) {
17802
+ emit({ type: "error", message: t2("Allow popups to open this resource in a new window.") });
17803
+ }
17804
+ }
17805
+ } catch (error) {
17806
+ popup?.close();
17807
+ emit({ type: "error", requestId: message.requestId, message: error.message });
17808
+ }
17809
+ return;
17810
+ }
17591
17811
  if (message.type === "trace") {
17592
17812
  if (trace.open(message)) {
17593
17813
  return;
@@ -18367,7 +18587,7 @@ ${state.user?.avatarUrl ?? ""}`;
18367
18587
  plan: (text3) => dialogs.plan(text3),
18368
18588
  file: (id) => post({ type: "open-file", id }),
18369
18589
  image: (src, title) => dialogs.image(src, title),
18370
- resource: (card) => post({ type: "resource", messageId: view.message.id, cardId: card.id })
18590
+ resource: (card, action, requestId2) => post({ type: "resource", messageId: view.message.id, cardId: card.id, action, requestId: requestId2 })
18371
18591
  });
18372
18592
  const actions = root.querySelector(".message-actions");
18373
18593
  actions.prepend(iconButton("history", t2("Trace"), () => post({ type: "trace", messageId: view.message.id, runId: view.message.runId })));
@@ -18398,7 +18618,7 @@ ${state.user?.avatarUrl ?? ""}`;
18398
18618
  if (message.role === "user") {
18399
18619
  view.content.textContent = message.content;
18400
18620
  } else {
18401
- view.output.update(message, state.project.id, state.activeId, (currentSession()?.messages ?? []).flatMap((entry) => [...entry.files ?? [], ...entry.attachments ?? []]));
18621
+ view.output.update(message, state.project.id, state.activeId, (currentSession()?.messages ?? []).flatMap((entry) => [...entry.files ?? [], ...entry.attachments ?? []]), state.project.projectInfo?.dataStorage?.mode === "server" ? "server" : "local");
18402
18622
  }
18403
18623
  view.error.textContent = message.error ?? "";
18404
18624
  view.error.hidden = !message.error;
@@ -18489,6 +18709,9 @@ ${state.user?.avatarUrl ?? ""}`;
18489
18709
  draftFiles = session.draft?.attachments ?? [];
18490
18710
  editingQueueId = session.draft?.editingQueueId;
18491
18711
  renderedSession = next.activeId;
18712
+ for (const view of messages.values()) {
18713
+ view.output?.dispose();
18714
+ }
18492
18715
  messages.clear();
18493
18716
  transcript.replaceChildren();
18494
18717
  document.querySelector("#error").hidden = true;
@@ -18531,6 +18754,7 @@ ${state.user?.avatarUrl ?? ""}`;
18531
18754
  });
18532
18755
  for (const [id, view] of messages) {
18533
18756
  if (!active.has(id)) {
18757
+ view.output?.dispose();
18534
18758
  view.root.remove();
18535
18759
  messages.delete(id);
18536
18760
  }