viveworker 0.4.6 → 0.4.8

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.
@@ -107,8 +107,11 @@ switch (hookEventName) {
107
107
  case "PostToolUseFailure":
108
108
  await handlePostToolUse();
109
109
  break;
110
+ case "UserPromptSubmit":
111
+ await handleUserPromptSubmit();
112
+ break;
110
113
  default:
111
- // Notification, Stop, UserPromptSubmit, SessionEnd
114
+ // Notification, Stop, SessionEnd
112
115
  await postEvent(hookEventName, { sessionId, toolName, cwd }, DEFAULT_EVENT_TIMEOUT_MS);
113
116
  break;
114
117
  }
@@ -399,6 +402,76 @@ async function handlePostToolUse() {
399
402
  );
400
403
  }
401
404
 
405
+ // ---------------------------------------------------------------------------
406
+ // UserPromptSubmit — thread inbox auto-read
407
+ // ---------------------------------------------------------------------------
408
+
409
+ async function handleUserPromptSubmit() {
410
+ // Check ~/.viveworker/thread-inbox/ for pending shared content from other
411
+ // AI tool sessions. If files are found, inject their content as additional
412
+ // context so the assistant sees them automatically.
413
+ //
414
+ // Targeting: if an inbox file has a `targetCwd` field, only consume it when
415
+ // the current session's cwd starts with that path (project match). Files
416
+ // without targetCwd are consumed by any session.
417
+ const inboxDir = path.join(os.homedir(), ".viveworker", "thread-inbox");
418
+ let inboxFiles = [];
419
+ try {
420
+ const entries = await fs.readdir(inboxDir);
421
+ inboxFiles = entries.filter((e) => e.endsWith(".json"));
422
+ } catch {
423
+ // Inbox dir doesn't exist or is unreadable — nothing to inject.
424
+ }
425
+
426
+ if (inboxFiles.length > 0) {
427
+ const parts = [];
428
+ for (const file of inboxFiles) {
429
+ const filePath = path.join(inboxDir, file);
430
+ try {
431
+ const raw = await fs.readFile(filePath, "utf8");
432
+ const share = JSON.parse(raw);
433
+ // Skip files targeted at a different project.
434
+ if (share.targetCwd && !cwd.startsWith(share.targetCwd)) {
435
+ continue;
436
+ }
437
+ const sourceLabel = share.sourceLabel || share.sourceTool || "another session";
438
+ const header = `[Shared from ${sourceLabel}]`;
439
+ let body = share.content || "";
440
+ // Append context file references if the content doesn't already include them.
441
+ const ctxFiles = Array.isArray(share.contextFiles) ? share.contextFiles.filter(Boolean) : [];
442
+ if (ctxFiles.length > 0 && !body.includes("Context files")) {
443
+ const fileList = ctxFiles.map((f) => `- ${f}`).join("\n");
444
+ body += `\n\n## Context files\n\nRead the following files for full conversation context:\n${fileList}`;
445
+ }
446
+ parts.push(`${header}\n\n${body}`);
447
+ // Mark as consumed by removing the file.
448
+ await fs.unlink(filePath);
449
+ } catch {
450
+ // Skip malformed or unreadable files.
451
+ }
452
+ }
453
+
454
+ if (parts.length > 0) {
455
+ // Cap at 10,000 chars (Claude Code hook output limit).
456
+ let combined = parts.join("\n\n---\n\n");
457
+ if (combined.length > 9800) {
458
+ combined = combined.slice(0, 9800) + "\n\n… (truncated)";
459
+ }
460
+ process.stdout.write(
461
+ JSON.stringify({
462
+ hookSpecificOutput: {
463
+ hookEventName: "UserPromptSubmit",
464
+ additionalContext: combined,
465
+ },
466
+ }) + "\n"
467
+ );
468
+ }
469
+ }
470
+
471
+ // Forward event to bridge as usual.
472
+ await postEvent("UserPromptSubmit", { sessionId, toolName, cwd }, DEFAULT_EVENT_TIMEOUT_MS);
473
+ }
474
+
402
475
  // ---------------------------------------------------------------------------
403
476
  // Diff computation
404
477
  // ---------------------------------------------------------------------------
package/web/app.css CHANGED
@@ -524,7 +524,9 @@ code {
524
524
 
525
525
  .settings-nav-row + .settings-nav-row,
526
526
  .settings-info-row + .settings-info-row,
527
- .settings-choice-row + .settings-choice-row {
527
+ .settings-choice-row + .settings-choice-row,
528
+ .settings-radio-row + .settings-radio-row,
529
+ .settings-info-row + .settings-radio-row {
528
530
  border-top: 1px solid rgba(255, 255, 255, 0.06);
529
531
  }
530
532
 
@@ -533,6 +535,80 @@ code {
533
535
  background: rgba(255, 255, 255, 0.035);
534
536
  }
535
537
 
538
+ .settings-group__description {
539
+ margin: 0;
540
+ padding: 0.72rem 1rem;
541
+ font-size: 0.83rem;
542
+ line-height: 1.45;
543
+ color: var(--muted);
544
+ border-bottom: 1px solid rgba(255, 255, 255, 0.06);
545
+ }
546
+
547
+ .settings-radio-row {
548
+ display: flex;
549
+ align-items: center;
550
+ gap: 0.78rem;
551
+ padding: 0.82rem 1rem;
552
+ cursor: pointer;
553
+ -webkit-tap-highlight-color: transparent;
554
+ }
555
+
556
+ .settings-radio-row:active {
557
+ background: rgba(255, 255, 255, 0.035);
558
+ }
559
+
560
+ .settings-radio-row input[type="radio"] {
561
+ appearance: none;
562
+ -webkit-appearance: none;
563
+ width: 1.25rem;
564
+ height: 1.25rem;
565
+ border: 2px solid rgba(156, 181, 197, 0.32);
566
+ border-radius: 50%;
567
+ background: transparent;
568
+ flex-shrink: 0;
569
+ position: relative;
570
+ cursor: pointer;
571
+ transition: border-color 160ms ease;
572
+ }
573
+
574
+ .settings-radio-row input[type="radio"]:checked {
575
+ border-color: rgba(121, 196, 255, 0.92);
576
+ }
577
+
578
+ .settings-radio-row input[type="radio"]:checked::after {
579
+ content: "";
580
+ position: absolute;
581
+ top: 50%;
582
+ left: 50%;
583
+ transform: translate(-50%, -50%);
584
+ width: 0.52rem;
585
+ height: 0.52rem;
586
+ border-radius: 50%;
587
+ background: rgba(121, 196, 255, 0.92);
588
+ }
589
+
590
+ .settings-radio-row input[type="radio"]:disabled {
591
+ opacity: 0.35;
592
+ cursor: not-allowed;
593
+ }
594
+
595
+ .settings-radio-row__label {
596
+ font-size: 0.94rem;
597
+ line-height: 1.25;
598
+ color: var(--text);
599
+ }
600
+
601
+ .settings-radio-row__label.muted {
602
+ color: var(--muted);
603
+ }
604
+
605
+ .settings-radio-row__badge {
606
+ margin-left: auto;
607
+ font-size: 0.74rem;
608
+ letter-spacing: 0.02em;
609
+ color: rgba(121, 196, 255, 0.82);
610
+ }
611
+
536
612
  .settings-row__icon {
537
613
  width: 1.95rem;
538
614
  height: 1.95rem;
@@ -2354,6 +2430,24 @@ code {
2354
2430
  border-color: var(--accent, #6ea8ff);
2355
2431
  }
2356
2432
 
2433
+ .a2a-task-response {
2434
+ width: 100%;
2435
+ padding: 0.85rem 0.95rem;
2436
+ border-radius: 14px;
2437
+ border: 1px solid var(--line);
2438
+ background: var(--surface, #1a1a2e);
2439
+ color: var(--fg);
2440
+ font-family: "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
2441
+ font-size: 0.85rem;
2442
+ line-height: 1.55;
2443
+ white-space: pre-wrap;
2444
+ word-break: break-word;
2445
+ overflow-x: auto;
2446
+ max-height: 24rem;
2447
+ overflow-y: auto;
2448
+ margin: 0;
2449
+ }
2450
+
2357
2451
  .compose-greeting {
2358
2452
  display: flex;
2359
2453
  align-items: center;
@@ -2655,6 +2749,34 @@ code {
2655
2749
  line-height: 1.45;
2656
2750
  }
2657
2751
 
2752
+ .reply-mode-switch--settings {
2753
+ grid-template-columns: minmax(0, 1fr) auto;
2754
+ align-items: start;
2755
+ }
2756
+
2757
+ .reply-mode-switch--settings__toggle {
2758
+ grid-column: 2;
2759
+ grid-row: 1;
2760
+ display: flex;
2761
+ flex-direction: column;
2762
+ align-items: center;
2763
+ gap: 0.32rem;
2764
+ }
2765
+
2766
+ .reply-mode-switch--settings .reply-mode-switch__hint {
2767
+ grid-column: 1;
2768
+ grid-row: 1;
2769
+ }
2770
+
2771
+ .reply-mode-switch__input:checked ~ .reply-mode-switch--settings__toggle .reply-mode-switch__track {
2772
+ background: rgba(67, 160, 255, 0.28);
2773
+ border-color: rgba(121, 196, 255, 0.34);
2774
+ }
2775
+
2776
+ .reply-mode-switch__input:checked ~ .reply-mode-switch--settings__toggle .reply-mode-switch__thumb {
2777
+ transform: translateX(1.06rem);
2778
+ }
2779
+
2658
2780
  @keyframes detail-loading-pulse {
2659
2781
  0% {
2660
2782
  background-position: 100% 0;