devez-vibe 1.7.63 → 1.7.65

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.
package/bin/dvz.exe CHANGED
Binary file
@@ -122,6 +122,9 @@ function sanitizedEnvironment() {
122
122
  delete env.ANTHROPIC_API_KEY;
123
123
  delete env.ANTHROPIC_AUTH_TOKEN;
124
124
  env.CLAUDE_AGENT_SDK_CLIENT_APP = `dvz/${VERSION}`;
125
+ // Claude Code leaves the Artifact tool off for SDK hosts unless asked; the
126
+ // account flag and admin policy still decide whether it actually appears.
127
+ env.CLAUDE_CODE_ARTIFACT ??= "1";
125
128
  return env;
126
129
  }
127
130
 
@@ -2486,6 +2489,14 @@ function processUser(session, message) {
2486
2489
  finishSubagentTask(session, firstLine(pending.input?.task_id || "", 80));
2487
2490
  }
2488
2491
  if (!pending) continue;
2492
+ if (pending.name === "Artifact" && typeof message.tool_use_result?.url === "string") {
2493
+ notify("turn/artifact/published", {
2494
+ threadId: session.id,
2495
+ url: message.tool_use_result.url,
2496
+ title: String(message.tool_use_result.title || ""),
2497
+ path: String(message.tool_use_result.path || pending.input?.file_path || ""),
2498
+ });
2499
+ }
2489
2500
  pending.toolUseId = block.tool_use_id;
2490
2501
  if (pending.suppressed) {
2491
2502
  updatePlanFromToolResult(session, pending, message);
@@ -3135,11 +3146,45 @@ function transcriptEntries(raw) {
3135
3146
  if (seen.has(entry.uuid)) continue;
3136
3147
  seen.add(entry.uuid);
3137
3148
  }
3149
+ // The transcript file spells the SDK's tool_use_result as toolUseResult.
3150
+ if (entry.tool_use_result === undefined && entry.toolUseResult !== undefined) entry.tool_use_result = entry.toolUseResult;
3138
3151
  messages.push(entry);
3139
3152
  }
3140
3153
  return messages;
3141
3154
  }
3142
3155
 
3156
+ // Pages the transcript published with the Artifact tool, oldest first and each
3157
+ // URL at its latest publish, so a resumed session gets its row under the status
3158
+ // line back the way the CLI rehydrates its own footer.
3159
+ function publishedArtifacts(messages) {
3160
+ const inputs = new Map();
3161
+ const artifacts = new Map();
3162
+ for (const message of messages) {
3163
+ for (const block of contentBlocks(message.message)) {
3164
+ if (block.type === "tool_use" && block.name === "Artifact") inputs.set(block.id, block.input || {});
3165
+ if (block.type !== "tool_result" || !inputs.has(block.tool_use_id)) continue;
3166
+ const result = message.tool_use_result;
3167
+ if (typeof result?.url !== "string") continue;
3168
+ artifacts.delete(result.url);
3169
+ artifacts.set(result.url, {
3170
+ url: result.url,
3171
+ title: String(result.title || ""),
3172
+ path: String(result.path || inputs.get(block.tool_use_id).file_path || ""),
3173
+ });
3174
+ }
3175
+ }
3176
+ return [...artifacts.values()];
3177
+ }
3178
+
3179
+ // Sent after the resume reply, which the host has already cleared its row for.
3180
+ function restoreArtifacts(id, messages) {
3181
+ const artifacts = publishedArtifacts(messages);
3182
+ if (!artifacts.length) return;
3183
+ setImmediate(() => {
3184
+ for (const artifact of artifacts) notify("turn/artifact/published", { threadId: id, ...artifact });
3185
+ });
3186
+ }
3187
+
3143
3188
  async function transcriptMessagesAt(path) {
3144
3189
  let raw;
3145
3190
  try {
@@ -3370,6 +3415,7 @@ async function dispatch(method, params = {}) {
3370
3415
  if (existing) {
3371
3416
  const messages = await sessionMessages(id, existing.cwd);
3372
3417
  if (!existing.tasks.size) existing.tasks = historyState(messages).tasks;
3418
+ restoreArtifacts(id, messages);
3373
3419
  return {
3374
3420
  id,
3375
3421
  thread: { id, turns: [] },
@@ -3403,6 +3449,7 @@ async function dispatch(method, params = {}) {
3403
3449
  session.lastContextUsage = tokenUsage?.last || null;
3404
3450
  session.lastContextWindow = tokenUsage?.modelContextWindow || 0;
3405
3451
  session.tasks = historyState(messages).tasks;
3452
+ restoreArtifacts(id, messages);
3406
3453
  return {
3407
3454
  id,
3408
3455
  thread: { id, turns: [] },
@@ -3633,6 +3680,25 @@ async function runSelfTest() {
3633
3680
  || JSON.stringify(restoredPrompts) !== JSON.stringify(["compact 이전 질문", "compact 이후 질문"])) {
3634
3681
  throw new Error(`Claude compacted transcript self-test failed: ${JSON.stringify({ restored: restored.length, restoredPrompts })}`);
3635
3682
  }
3683
+ // A resumed transcript lists its Artifact pages once each at their latest
3684
+ // publish, read through the file's toolUseResult spelling.
3685
+ const artifactTranscript = [
3686
+ JSON.stringify({ type: "assistant", uuid: "art-1", message: { role: "assistant", content: [{ type: "tool_use", id: "toolu_a1", name: "Artifact", input: { file_path: "C:\\tmp\\first.html" } }] } }),
3687
+ JSON.stringify({ type: "user", uuid: "art-1r", message: { role: "user", content: [{ type: "tool_result", tool_use_id: "toolu_a1", content: "ok" }] }, toolUseResult: { url: "https://claude.ai/code/artifact/one", title: "One" } }),
3688
+ JSON.stringify({ type: "assistant", uuid: "art-2", message: { role: "assistant", content: [{ type: "tool_use", id: "toolu_a2", name: "Artifact", input: { file_path: "C:\\tmp\\second.html" } }] } }),
3689
+ JSON.stringify({ type: "user", uuid: "art-2r", message: { role: "user", content: [{ type: "tool_result", tool_use_id: "toolu_a2", content: "ok" }] }, toolUseResult: { url: "https://claude.ai/code/artifact/two", path: "C:\\tmp\\second.html", title: "Two" } }),
3690
+ JSON.stringify({ type: "assistant", uuid: "art-3", message: { role: "assistant", content: [{ type: "tool_use", id: "toolu_a3", name: "Artifact", input: { action: "read", url: "https://claude.ai/code/artifact/one" } }] } }),
3691
+ JSON.stringify({ type: "user", uuid: "art-3r", message: { role: "user", content: [{ type: "tool_result", tool_use_id: "toolu_a3", content: "<html>" }] }, toolUseResult: { content: "<html>" } }),
3692
+ JSON.stringify({ type: "assistant", uuid: "art-4", message: { role: "assistant", content: [{ type: "tool_use", id: "toolu_a4", name: "Artifact", input: { file_path: "C:\\tmp\\first.html" } }] } }),
3693
+ JSON.stringify({ type: "user", uuid: "art-4r", message: { role: "user", content: [{ type: "tool_result", tool_use_id: "toolu_a4", content: "ok" }] }, toolUseResult: { url: "https://claude.ai/code/artifact/one", path: "C:\\tmp\\first.html", title: "One", updated: true } }),
3694
+ ].join("\n");
3695
+ const restoredArtifacts = publishedArtifacts(transcriptEntries(artifactTranscript));
3696
+ if (JSON.stringify(restoredArtifacts) !== JSON.stringify([
3697
+ { url: "https://claude.ai/code/artifact/two", title: "Two", path: "C:\\tmp\\second.html" },
3698
+ { url: "https://claude.ai/code/artifact/one", title: "One", path: "C:\\tmp\\first.html" },
3699
+ ])) {
3700
+ throw new Error(`Claude artifact resume self-test failed: ${JSON.stringify(restoredArtifacts)}`);
3701
+ }
3636
3702
  // A model that left the CLI's list resolves to its family's newest entry, so a
3637
3703
  // Fable 5 transcript resumes on Fable 5.1 instead of the host's default.
3638
3704
  const successorModels = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devez-vibe",
3
- "version": "1.7.63",
3
+ "version": "1.7.65",
4
4
  "description": "Stable terminal UI for Codex and Claude Agent SDK",
5
5
  "keywords": [
6
6
  "codex",