viveworker 0.4.7 → 0.4.9
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/package.json +1 -1
- package/scripts/a2a-executor.mjs +267 -41
- package/scripts/a2a-handler.mjs +6 -1
- package/scripts/a2a-relay-client.mjs +13 -0
- package/scripts/moltbook-api.mjs +214 -5
- package/scripts/moltbook-cli.mjs +12 -397
- package/scripts/viveworker-bridge.mjs +471 -47
- package/web/app.css +249 -1
- package/web/app.js +307 -55
- package/web/i18n.js +69 -5
package/web/app.js
CHANGED
|
@@ -5,7 +5,7 @@ const INSTALL_BANNER_DISMISS_KEY = "viveworker-install-banner-dismissed-v2";
|
|
|
5
5
|
const PUSH_BANNER_DISMISS_KEY = "viveworker-push-banner-dismissed-v1";
|
|
6
6
|
const INITIAL_DETECTED_LOCALE = detectBrowserLocale();
|
|
7
7
|
const TIMELINE_MESSAGE_KINDS = new Set(["user_message", "assistant_commentary", "assistant_final"]);
|
|
8
|
-
const TIMELINE_OPERATIONAL_KINDS = new Set(["approval", "plan", "plan_ready", "choice"
|
|
8
|
+
const TIMELINE_OPERATIONAL_KINDS = new Set(["approval", "plan", "plan_ready", "choice"]);
|
|
9
9
|
const THREAD_FILTER_INTERACTION_DEFER_MS = 8000;
|
|
10
10
|
const MAX_COMPLETION_REPLY_IMAGE_COUNT = 4;
|
|
11
11
|
const NOTIFICATION_INTENT_CACHE = "viveworker-notification-intent-v1";
|
|
@@ -48,7 +48,9 @@ const state = {
|
|
|
48
48
|
pairNotice: "",
|
|
49
49
|
pushStatus: null,
|
|
50
50
|
moltbookScoutStatus: null,
|
|
51
|
+
moltbookRecentTitlesExpanded: 0,
|
|
51
52
|
a2aRelayStatus: null,
|
|
53
|
+
a2aTaskExecutorPick: "codex",
|
|
52
54
|
pushNotice: "",
|
|
53
55
|
pushError: "",
|
|
54
56
|
deviceNotice: "",
|
|
@@ -612,12 +614,19 @@ function filteredDiffEntries() {
|
|
|
612
614
|
}
|
|
613
615
|
|
|
614
616
|
function syncTimelineThreadFilter() {
|
|
615
|
-
const threads = Array.isArray(state.timeline?.threads) ? state.timeline.threads : [];
|
|
616
617
|
if (!state.timelineThreadFilter || state.timelineThreadFilter === "all") {
|
|
617
618
|
state.timelineThreadFilter = "all";
|
|
618
619
|
return;
|
|
619
620
|
}
|
|
620
|
-
|
|
621
|
+
const provider = state.providerFilter || "all";
|
|
622
|
+
let validThreadIds;
|
|
623
|
+
if (provider === "all") {
|
|
624
|
+
const threads = Array.isArray(state.timeline?.threads) ? state.timeline.threads : [];
|
|
625
|
+
validThreadIds = new Set(threads.map((t) => t.id));
|
|
626
|
+
} else {
|
|
627
|
+
validThreadIds = new Set(timelineThreadsForProvider(provider).map((t) => t.id));
|
|
628
|
+
}
|
|
629
|
+
if (!validThreadIds.has(state.timelineThreadFilter)) {
|
|
621
630
|
state.timelineThreadFilter = "all";
|
|
622
631
|
}
|
|
623
632
|
}
|
|
@@ -630,16 +639,47 @@ function syncTimelineKindFilter() {
|
|
|
630
639
|
}
|
|
631
640
|
|
|
632
641
|
function timelineKindFilterOptions() {
|
|
633
|
-
|
|
634
|
-
|
|
642
|
+
const provider = state.providerFilter || "all";
|
|
643
|
+
const allOption = { id: "all", label: L("timeline.kindFilter.all"), icon: "filter" };
|
|
644
|
+
|
|
645
|
+
if (provider === "moltbook") {
|
|
646
|
+
return [
|
|
647
|
+
allOption,
|
|
648
|
+
{ id: "moltbook_reply_drafts", label: L("timeline.kindFilter.moltbookReplyDrafts"), icon: "moltbook-reply" },
|
|
649
|
+
{ id: "moltbook_post_drafts", label: L("timeline.kindFilter.moltbookPostDrafts"), icon: "moltbook-draft" },
|
|
650
|
+
{ id: "moltbook_comments", label: L("timeline.kindFilter.moltbookComments"), icon: "moltbook-comment" },
|
|
651
|
+
];
|
|
652
|
+
}
|
|
653
|
+
if (provider === "a2a") {
|
|
654
|
+
return [
|
|
655
|
+
allOption,
|
|
656
|
+
{ id: "a2a_requests", label: L("timeline.kindFilter.a2aRequests"), icon: "item" },
|
|
657
|
+
{ id: "a2a_results", label: L("timeline.kindFilter.a2aResults"), icon: "completion-item" },
|
|
658
|
+
];
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
const codexClaudeOptions = [
|
|
662
|
+
allOption,
|
|
635
663
|
{ id: "messages", label: L("timeline.kindFilter.messages"), icon: "timeline" },
|
|
636
664
|
{ id: "files", label: L("timeline.kindFilter.files"), icon: "file-event" },
|
|
637
665
|
{ id: "approvals", label: L("timeline.kindFilter.approvals"), icon: "approval" },
|
|
638
666
|
{ id: "plans", label: L("timeline.kindFilter.plans"), icon: "plan" },
|
|
639
667
|
{ id: "choices", label: L("timeline.kindFilter.choices"), icon: "choice" },
|
|
640
|
-
{ id: "
|
|
641
|
-
|
|
668
|
+
{ id: "final_answers", label: L("timeline.kindFilter.finalAnswers"), icon: "assistant-final" },
|
|
669
|
+
];
|
|
670
|
+
|
|
671
|
+
if (provider === "codex" || provider === "claude") {
|
|
672
|
+
return codexClaudeOptions;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// "all" — union of everything
|
|
676
|
+
return [
|
|
677
|
+
...codexClaudeOptions,
|
|
678
|
+
{ id: "moltbook_reply_drafts", label: L("timeline.kindFilter.moltbookReplyDrafts"), icon: "moltbook-reply" },
|
|
679
|
+
{ id: "moltbook_post_drafts", label: L("timeline.kindFilter.moltbookPostDrafts"), icon: "moltbook-draft" },
|
|
642
680
|
{ id: "moltbook_comments", label: L("timeline.kindFilter.moltbookComments"), icon: "moltbook-comment" },
|
|
681
|
+
{ id: "a2a_requests", label: L("timeline.kindFilter.a2aRequests"), icon: "item" },
|
|
682
|
+
{ id: "a2a_results", label: L("timeline.kindFilter.a2aResults"), icon: "completion-item" },
|
|
643
683
|
];
|
|
644
684
|
}
|
|
645
685
|
|
|
@@ -665,10 +705,20 @@ function timelineEntryMatchesKindFilter(entry, filterId) {
|
|
|
665
705
|
return kind === "choice";
|
|
666
706
|
case "completions":
|
|
667
707
|
return kind === "completion";
|
|
708
|
+
case "final_answers":
|
|
709
|
+
return kind === "assistant_final";
|
|
668
710
|
case "moltbook_drafts":
|
|
669
711
|
return kind === "moltbook_draft";
|
|
712
|
+
case "moltbook_reply_drafts":
|
|
713
|
+
return kind === "moltbook_draft" && entry?.draftType === "reply";
|
|
714
|
+
case "moltbook_post_drafts":
|
|
715
|
+
return kind === "moltbook_draft" && entry?.draftType !== "reply";
|
|
670
716
|
case "moltbook_comments":
|
|
671
717
|
return kind === "moltbook_reply";
|
|
718
|
+
case "a2a_requests":
|
|
719
|
+
return kind === "a2a_task";
|
|
720
|
+
case "a2a_results":
|
|
721
|
+
return kind === "a2a_task_result";
|
|
672
722
|
default:
|
|
673
723
|
return true;
|
|
674
724
|
}
|
|
@@ -689,10 +739,19 @@ function completedThreads() {
|
|
|
689
739
|
if (!items.length) {
|
|
690
740
|
return [];
|
|
691
741
|
}
|
|
742
|
+
const provider = state.providerFilter || "all";
|
|
692
743
|
const byThread = new Map();
|
|
693
744
|
for (const item of items) {
|
|
694
745
|
const threadId = normalizeClientText(item.threadId || "");
|
|
695
|
-
if (!threadId
|
|
746
|
+
if (!threadId) {
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
// Skip Moltbook threads unless Moltbook tab is active
|
|
750
|
+
if (provider !== "moltbook" && isMoltbookThreadId(threadId, item)) {
|
|
751
|
+
continue;
|
|
752
|
+
}
|
|
753
|
+
// Filter by provider
|
|
754
|
+
if (provider !== "all" && normalizeProviderClient(item.provider) !== provider) {
|
|
696
755
|
continue;
|
|
697
756
|
}
|
|
698
757
|
const latestAtMs = Number(item.createdAtMs || 0);
|
|
@@ -1375,7 +1434,6 @@ function buildDetailLoadingSnapshot(itemRef = state.currentItem) {
|
|
|
1375
1434
|
readOnly:
|
|
1376
1435
|
entry?.status === "completed" ||
|
|
1377
1436
|
TIMELINE_MESSAGE_KINDS.has(itemRef.kind) ||
|
|
1378
|
-
itemRef.kind === "completion" ||
|
|
1379
1437
|
(itemRef.kind === "choice" && item.supported === false),
|
|
1380
1438
|
loading: true,
|
|
1381
1439
|
};
|
|
@@ -1779,11 +1837,13 @@ function renderProviderFilter() {
|
|
|
1779
1837
|
`;
|
|
1780
1838
|
}
|
|
1781
1839
|
|
|
1840
|
+
const COMPLETED_CARD_KINDS = new Set(["assistant_final", "approval", "moltbook_reply", "moltbook_draft", "a2a_task_result", "thread_share"]);
|
|
1841
|
+
|
|
1782
1842
|
function renderItemCard(entry, sourceTab, desktop) {
|
|
1783
|
-
if (entry.status === "completed" && entry.item.kind
|
|
1843
|
+
if (entry.status === "completed" && COMPLETED_CARD_KINDS.has(entry.item.kind)) {
|
|
1784
1844
|
return renderCompletedCompletionCard(entry, sourceTab);
|
|
1785
1845
|
}
|
|
1786
|
-
const kindInfo = kindMeta(entry.item.kind);
|
|
1846
|
+
const kindInfo = kindMeta(entry.item.kind, entry.item);
|
|
1787
1847
|
const cardTitle = cardTitleForEntry(entry);
|
|
1788
1848
|
const statusText = entry.status === "completed" ? L("common.completed") : L("common.actionNeeded");
|
|
1789
1849
|
const intentText = itemIntentText(entry.item.kind, entry.status, entry.item.provider);
|
|
@@ -1852,10 +1912,12 @@ function cardTitleForEntry(entry) {
|
|
|
1852
1912
|
|
|
1853
1913
|
function renderCompletedCompletionCard(entry, sourceTab) {
|
|
1854
1914
|
const item = entry.item;
|
|
1855
|
-
const kindInfo = kindMeta(item.kind);
|
|
1915
|
+
const kindInfo = kindMeta(item.kind, item);
|
|
1856
1916
|
const summaryText = item.summary || fallbackSummaryForKind(item.kind, entry.status, item.provider);
|
|
1857
1917
|
const threadLabel = timelineEntryThreadLabel(item, true);
|
|
1858
1918
|
const timestampLabel = formatTimelineTimestamp(item.createdAtMs);
|
|
1919
|
+
const pillLabel = item.kind === "completion" ? L("common.task") : kindInfo.label;
|
|
1920
|
+
const pillTone = item.kind === "completion" ? "completion" : kindInfo.tone;
|
|
1859
1921
|
|
|
1860
1922
|
return `
|
|
1861
1923
|
<button
|
|
@@ -1867,7 +1929,7 @@ function renderCompletedCompletionCard(entry, sourceTab) {
|
|
|
1867
1929
|
>
|
|
1868
1930
|
<div class="item-card__header">
|
|
1869
1931
|
<div class="item-card__meta">
|
|
1870
|
-
<span class="type-pill type-pill
|
|
1932
|
+
<span class="type-pill type-pill--${escapeHtml(pillTone)}">${escapeHtml(pillLabel)}</span>
|
|
1871
1933
|
${renderProviderBadge(item.provider)}
|
|
1872
1934
|
</div>
|
|
1873
1935
|
<div class="item-card__header-right">
|
|
@@ -1957,17 +2019,47 @@ function renderDiffPanel({ entries, desktop }) {
|
|
|
1957
2019
|
`;
|
|
1958
2020
|
}
|
|
1959
2021
|
|
|
2022
|
+
function timelineThreadsForProvider(provider) {
|
|
2023
|
+
const entries = Array.isArray(state.timeline?.entries) ? state.timeline.entries : [];
|
|
2024
|
+
const byThread = new Map();
|
|
2025
|
+
for (const entry of entries) {
|
|
2026
|
+
const threadId = entry.threadId || "";
|
|
2027
|
+
if (!threadId) continue;
|
|
2028
|
+
if (normalizeProviderClient(entry.provider) !== provider) continue;
|
|
2029
|
+
const latestAtMs = Number(entry.createdAtMs) || 0;
|
|
2030
|
+
const label = entry.threadLabel || "";
|
|
2031
|
+
const existing = byThread.get(threadId);
|
|
2032
|
+
if (!existing || latestAtMs > existing.latestAtMs) {
|
|
2033
|
+
byThread.set(threadId, { id: threadId, label, latestAtMs });
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
return [...byThread.values()]
|
|
2037
|
+
.sort((a, b) => b.latestAtMs - a.latestAtMs)
|
|
2038
|
+
.map((t) => ({ id: t.id, label: dropdownThreadLabel(t.id, t.label) }));
|
|
2039
|
+
}
|
|
2040
|
+
|
|
1960
2041
|
function renderTimelineThreadDropdown() {
|
|
1961
|
-
const
|
|
2042
|
+
const provider = state.providerFilter || "all";
|
|
2043
|
+
const kindFilterHtml = renderTimelineKindFilterControls();
|
|
2044
|
+
|
|
2045
|
+
let threads;
|
|
2046
|
+
if (provider === "all") {
|
|
2047
|
+
// "All" view — use the server-provided thread list
|
|
2048
|
+
threads = (Array.isArray(state.timeline?.threads) ? state.timeline.threads : []).map((thread) => ({
|
|
2049
|
+
id: thread.id,
|
|
2050
|
+
label: dropdownThreadLabel(thread.id, thread.label || ""),
|
|
2051
|
+
}));
|
|
2052
|
+
} else {
|
|
2053
|
+
// Provider-specific view — build from entries matching this provider
|
|
2054
|
+
threads = timelineThreadsForProvider(provider);
|
|
2055
|
+
}
|
|
2056
|
+
|
|
1962
2057
|
return renderThreadDropdown({
|
|
1963
2058
|
inputId: "timeline-thread-select",
|
|
1964
2059
|
dataAttribute: "data-timeline-thread-select",
|
|
1965
2060
|
selectedThreadId: state.timelineThreadFilter,
|
|
1966
|
-
controlsHtml:
|
|
1967
|
-
threads
|
|
1968
|
-
id: thread.id,
|
|
1969
|
-
label: dropdownThreadLabel(thread.id, thread.label || ""),
|
|
1970
|
-
})),
|
|
2061
|
+
controlsHtml: kindFilterHtml,
|
|
2062
|
+
threads,
|
|
1971
2063
|
});
|
|
1972
2064
|
}
|
|
1973
2065
|
|
|
@@ -2072,10 +2164,11 @@ function renderTimelineKindFilterControls() {
|
|
|
2072
2164
|
|
|
2073
2165
|
function renderTimelineEntry(entry, { desktop }) {
|
|
2074
2166
|
const item = entry.item;
|
|
2075
|
-
const kindInfo = kindMeta(item.kind);
|
|
2167
|
+
const kindInfo = kindMeta(item.kind, item);
|
|
2076
2168
|
const kindClassName = escapeHtml(kindInfo.tone || "neutral");
|
|
2077
2169
|
const kindNameClass = escapeHtml(String(item.kind || "item").replace(/_/gu, "-"));
|
|
2078
|
-
const
|
|
2170
|
+
const isMoltbookOrA2A = item.kind === "moltbook_reply" || item.kind === "moltbook_draft" || item.kind === "a2a_task" || item.kind === "a2a_task_result" || item.kind === "thread_share";
|
|
2171
|
+
const isMessageLike = TIMELINE_MESSAGE_KINDS.has(item.kind) || isMoltbookOrA2A;
|
|
2079
2172
|
const isFileEvent = item.kind === "file_event";
|
|
2080
2173
|
const imageUrls = Array.isArray(item.imageUrls) ? item.imageUrls.filter(Boolean) : [];
|
|
2081
2174
|
const fileRefs = normalizeClientFileRefs(item.fileRefs);
|
|
@@ -2932,6 +3025,9 @@ function settingsPageMeta(page) {
|
|
|
2932
3025
|
description: L("settings.a2aRelay.copy"),
|
|
2933
3026
|
icon: "link",
|
|
2934
3027
|
};
|
|
3028
|
+
case "a2aExecutor":
|
|
3029
|
+
// Executor settings integrated into a2aRelay page — redirect.
|
|
3030
|
+
return settingsPageMeta("a2aRelay");
|
|
2935
3031
|
default:
|
|
2936
3032
|
return settingsPageMeta("notifications");
|
|
2937
3033
|
}
|
|
@@ -3064,6 +3160,7 @@ function renderSettingsSubpage(context, { mobile }) {
|
|
|
3064
3160
|
content = renderSettingsMoltbookPage(context);
|
|
3065
3161
|
break;
|
|
3066
3162
|
case "a2aRelay":
|
|
3163
|
+
case "a2aExecutor":
|
|
3067
3164
|
content = renderSettingsA2aRelayPage(context);
|
|
3068
3165
|
break;
|
|
3069
3166
|
default:
|
|
@@ -3303,16 +3400,33 @@ function renderSettingsMoltbookPage(context) {
|
|
|
3303
3400
|
renderSettingsInfoRow(L("settings.row.moltbookSeenPosts"), String(scout.seenPostCount)),
|
|
3304
3401
|
])}
|
|
3305
3402
|
${batchRows.length ? renderSettingsGroup(L("settings.moltbook.batchTitle"), batchRows) : ""}
|
|
3306
|
-
${
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3403
|
+
${(() => {
|
|
3404
|
+
const titles = Array.isArray(scout.recentComposeTitles) ? scout.recentComposeTitles : [];
|
|
3405
|
+
if (!titles.length) return "";
|
|
3406
|
+
const PAGE_SIZE = 5;
|
|
3407
|
+
const visibleCount = state.moltbookRecentTitlesExpanded || PAGE_SIZE;
|
|
3408
|
+
const visible = titles.slice(0, visibleCount);
|
|
3409
|
+
const hasMore = titles.length > visibleCount;
|
|
3410
|
+
const rows = visible.map((t) => {
|
|
3411
|
+
const title = typeof t === "string" ? t : (t.title || "");
|
|
3412
|
+
const postId = typeof t === "object" ? t.postId : "";
|
|
3413
|
+
const type = typeof t === "object" ? (t.type || "post") : "post";
|
|
3414
|
+
const badge = type === "reply"
|
|
3415
|
+
? `<span class="settings-compose-badge settings-compose-badge--reply">${escapeHtml(L("settings.moltbook.typeReply"))}</span>`
|
|
3416
|
+
: `<span class="settings-compose-badge settings-compose-badge--post">${escapeHtml(L("settings.moltbook.typePost"))}</span>`;
|
|
3417
|
+
const link = postId
|
|
3418
|
+
? `<a href="https://www.moltbook.com/post/${escapeHtml(postId)}" target="_blank" rel="noopener">${escapeHtml(title)}</a>`
|
|
3419
|
+
: escapeHtml(title);
|
|
3420
|
+
return `<div class="settings-compose-entry">${badge}<span class="settings-compose-entry__title">${link}</span></div>`;
|
|
3421
|
+
});
|
|
3422
|
+
if (hasMore) {
|
|
3423
|
+
const remaining = titles.length - visibleCount;
|
|
3424
|
+
rows.push(`<button type="button" class="settings-compose-more" data-moltbook-titles-more>${escapeHtml(L("settings.moltbook.showMore", { count: remaining }))}</button>`);
|
|
3425
|
+
} else if (titles.length > PAGE_SIZE) {
|
|
3426
|
+
rows.push(`<button type="button" class="settings-compose-more" data-moltbook-titles-collapse>${escapeHtml(L("settings.moltbook.showLess"))}</button>`);
|
|
3427
|
+
}
|
|
3428
|
+
return renderSettingsGroup(L("settings.row.moltbookRecentTitles"), rows);
|
|
3429
|
+
})()}
|
|
3316
3430
|
</div>
|
|
3317
3431
|
`;
|
|
3318
3432
|
}
|
|
@@ -3334,17 +3448,60 @@ function renderSettingsA2aRelayPage(context) {
|
|
|
3334
3448
|
const profileUrl = `${relay.relayUrl}/${relay.userId}`;
|
|
3335
3449
|
const userIdLink = `<a href="${escapeHtml(profileUrl)}" target="_blank" rel="noopener">${escapeHtml(relay.userId)}</a>`;
|
|
3336
3450
|
const relayHost = (() => { try { return new URL(relay.relayUrl).host; } catch { return relay.relayUrl; } })();
|
|
3451
|
+
const publicChecked = relay.acceptPublicTasks === true;
|
|
3452
|
+
|
|
3453
|
+
// Executor preference section
|
|
3454
|
+
const executors = state.session?.a2aExecutors || { codex: false, claude: false };
|
|
3455
|
+
const currentExec = state.session?.a2aExecutorPreference || "ask";
|
|
3456
|
+
const bothAvailable = executors.codex && executors.claude;
|
|
3457
|
+
const execOptions = [
|
|
3458
|
+
{ id: "ask", label: L("settings.a2aExecutor.ask") },
|
|
3459
|
+
];
|
|
3460
|
+
if (executors.codex) execOptions.push({ id: "codex", label: L("settings.a2aExecutor.codex"), detected: true });
|
|
3461
|
+
if (executors.claude) execOptions.push({ id: "claude", label: L("settings.a2aExecutor.claude"), detected: true });
|
|
3462
|
+
if (bothAvailable) {
|
|
3463
|
+
execOptions.push({ id: "auto", label: L("settings.a2aExecutor.auto") });
|
|
3464
|
+
}
|
|
3465
|
+
|
|
3337
3466
|
return `
|
|
3338
3467
|
<div class="settings-page">
|
|
3339
|
-
${
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3468
|
+
${(() => {
|
|
3469
|
+
const stats = relay.taskStats || { received: 0, completed: 0, denied: 0 };
|
|
3470
|
+
return renderSettingsGroup("", [
|
|
3471
|
+
renderSettingsInfoRow(L("settings.row.a2aStatus"), statusLabel),
|
|
3472
|
+
renderSettingsInfoRow(L("settings.row.a2aUserId"), userIdLink, { rawValue: true }),
|
|
3473
|
+
renderSettingsInfoRow(L("settings.row.a2aRelay"), relayHost),
|
|
3474
|
+
renderSettingsInfoRow(L("settings.row.a2aApiKey"), relay.apiKeyConfigured ? L("settings.a2aRelay.apiKey.configured") : L("settings.a2aRelay.apiKey.notConfigured")),
|
|
3475
|
+
relay.lastPollAtMs
|
|
3476
|
+
? renderSettingsInfoRow(L("settings.row.a2aLastPoll"), new Date(relay.lastPollAtMs).toLocaleString(state.locale))
|
|
3477
|
+
: "",
|
|
3478
|
+
renderSettingsInfoRow(L("settings.row.a2aTaskReceived"), String(stats.received)),
|
|
3479
|
+
renderSettingsInfoRow(L("settings.row.a2aTaskCompleted"), String(stats.completed)),
|
|
3480
|
+
renderSettingsInfoRow(L("settings.row.a2aTaskDenied"), String(stats.denied)),
|
|
3481
|
+
].filter(Boolean));
|
|
3482
|
+
})()}
|
|
3483
|
+
<section class="settings-group">
|
|
3484
|
+
<p class="settings-group__title">${escapeHtml(L("settings.a2aRelay.publicTasks.title"))}</p>
|
|
3485
|
+
<label class="reply-mode-switch reply-mode-switch--settings" data-a2a-public-toggle>
|
|
3486
|
+
<input type="checkbox" class="reply-mode-switch__input" ${publicChecked ? "checked" : ""} data-a2a-public-checkbox />
|
|
3487
|
+
<span class="reply-mode-switch--settings__toggle">
|
|
3488
|
+
<span class="reply-mode-switch__track" aria-hidden="true"><span class="reply-mode-switch__thumb"></span></span>
|
|
3489
|
+
<span class="reply-mode-switch__state">${escapeHtml(publicChecked ? L("settings.claudeAway.on") : L("settings.claudeAway.off"))}</span>
|
|
3490
|
+
</span>
|
|
3491
|
+
<span class="reply-mode-switch__hint">${escapeHtml(L("settings.a2aRelay.publicTasks.description"))}</span>
|
|
3492
|
+
</label>
|
|
3493
|
+
</section>
|
|
3494
|
+
${renderSettingsGroup(L("settings.a2aExecutor.title"), [
|
|
3495
|
+
`<p class="settings-group__description">${escapeHtml(L("settings.a2aExecutor.copy"))}</p>`,
|
|
3496
|
+
...execOptions.map((opt) => `
|
|
3497
|
+
<label class="settings-radio-row" data-a2a-executor-option="${escapeHtml(opt.id)}">
|
|
3498
|
+
<input type="radio" name="a2aExecutor" value="${escapeHtml(opt.id)}"
|
|
3499
|
+
${currentExec === opt.id ? "checked" : ""} />
|
|
3500
|
+
<span class="settings-radio-row__label">${escapeHtml(opt.label)}</span>
|
|
3501
|
+
${opt.detected ? `<span class="settings-radio-row__badge">✓ ${escapeHtml(L("settings.a2aExecutor.detected"))}</span>` : ""}
|
|
3502
|
+
</label>
|
|
3503
|
+
`),
|
|
3504
|
+
])}
|
|
3348
3505
|
</div>
|
|
3349
3506
|
`;
|
|
3350
3507
|
}
|
|
@@ -3455,8 +3612,8 @@ function renderDetailContent(detail, { mobile }) {
|
|
|
3455
3612
|
}
|
|
3456
3613
|
|
|
3457
3614
|
function renderStandardDetailDesktop(detail) {
|
|
3458
|
-
const kindInfo = kindMeta(detail.kind);
|
|
3459
|
-
const spaciousBodyDetail = TIMELINE_MESSAGE_KINDS.has(detail.kind)
|
|
3615
|
+
const kindInfo = kindMeta(detail.kind, detail);
|
|
3616
|
+
const spaciousBodyDetail = TIMELINE_MESSAGE_KINDS.has(detail.kind);
|
|
3460
3617
|
const plainIntro = renderDetailPlainIntro(detail);
|
|
3461
3618
|
return `
|
|
3462
3619
|
<div class="detail-shell">
|
|
@@ -3470,7 +3627,7 @@ function renderStandardDetailDesktop(detail) {
|
|
|
3470
3627
|
${renderA2ATaskDetail(detail)}
|
|
3471
3628
|
${renderThreadShareDetail(detail)}
|
|
3472
3629
|
${
|
|
3473
|
-
detail.kind === "moltbook_draft" || detail.kind === "moltbook_reply" || detail.kind === "a2a_task" || detail.kind === "thread_share"
|
|
3630
|
+
detail.kind === "moltbook_draft" || detail.kind === "moltbook_reply" || detail.kind === "a2a_task" || detail.kind === "a2a_task_result" || detail.kind === "thread_share"
|
|
3474
3631
|
? ""
|
|
3475
3632
|
: plainIntro
|
|
3476
3633
|
? plainIntro
|
|
@@ -3493,8 +3650,8 @@ function renderStandardDetailDesktop(detail) {
|
|
|
3493
3650
|
}
|
|
3494
3651
|
|
|
3495
3652
|
function renderStandardDetailMobile(detail) {
|
|
3496
|
-
const kindInfo = kindMeta(detail.kind);
|
|
3497
|
-
const spaciousBodyDetail = TIMELINE_MESSAGE_KINDS.has(detail.kind)
|
|
3653
|
+
const kindInfo = kindMeta(detail.kind, detail);
|
|
3654
|
+
const spaciousBodyDetail = TIMELINE_MESSAGE_KINDS.has(detail.kind);
|
|
3498
3655
|
const plainIntro = renderDetailPlainIntro(detail, { mobile: true });
|
|
3499
3656
|
return `
|
|
3500
3657
|
<div class="mobile-detail-screen">
|
|
@@ -3508,7 +3665,7 @@ function renderStandardDetailMobile(detail) {
|
|
|
3508
3665
|
${renderA2ATaskDetail(detail, { mobile: true })}
|
|
3509
3666
|
${renderThreadShareDetail(detail, { mobile: true })}
|
|
3510
3667
|
${
|
|
3511
|
-
detail.kind === "moltbook_draft" || detail.kind === "moltbook_reply" || detail.kind === "a2a_task" || detail.kind === "thread_share"
|
|
3668
|
+
detail.kind === "moltbook_draft" || detail.kind === "moltbook_reply" || detail.kind === "a2a_task" || detail.kind === "a2a_task_result" || detail.kind === "thread_share"
|
|
3512
3669
|
? ""
|
|
3513
3670
|
: plainIntro
|
|
3514
3671
|
? plainIntro
|
|
@@ -4066,7 +4223,7 @@ function renderMoltbookDraftComposer(detail, options = {}) {
|
|
|
4066
4223
|
}
|
|
4067
4224
|
|
|
4068
4225
|
function renderA2ATaskDetail(detail, options = {}) {
|
|
4069
|
-
if (detail.kind !== "a2a_task") return "";
|
|
4226
|
+
if (detail.kind !== "a2a_task" && detail.kind !== "a2a_task_result") return "";
|
|
4070
4227
|
const enabled = detail.a2aTaskEnabled !== false && detail.readOnly !== true;
|
|
4071
4228
|
const instruction = detail.instruction || "";
|
|
4072
4229
|
const callerIp = detail.callerInfo?.ip || "";
|
|
@@ -4084,10 +4241,33 @@ function renderA2ATaskDetail(detail, options = {}) {
|
|
|
4084
4241
|
rejected: "a2a.task.statusRejected",
|
|
4085
4242
|
}[detail.taskStatus] || "a2a.task.statusSubmitted";
|
|
4086
4243
|
|
|
4087
|
-
const statusBadge = !enabled
|
|
4244
|
+
const statusBadge = !enabled && detail.kind === "a2a_task_result"
|
|
4088
4245
|
? `<span class="eyebrow-pill eyebrow-pill--subtle">${escapeHtml(L(statusKey))}</span>`
|
|
4089
4246
|
: "";
|
|
4090
4247
|
|
|
4248
|
+
// Show executor selector when "ask" mode is active and both CLIs are available.
|
|
4249
|
+
const executors = state.session?.a2aExecutors || { codex: false, claude: false };
|
|
4250
|
+
const executorPref = state.session?.a2aExecutorPreference || "auto";
|
|
4251
|
+
const showExecutorPicker = enabled && executorPref === "ask" && executors.codex && executors.claude;
|
|
4252
|
+
const pickedExecutor = state.a2aTaskExecutorPick || "codex";
|
|
4253
|
+
const executorPicker = showExecutorPicker
|
|
4254
|
+
? `
|
|
4255
|
+
<div class="reply-composer__instruction">
|
|
4256
|
+
<label class="field-label">${escapeHtml(L("a2a.task.executor"))}</label>
|
|
4257
|
+
<div class="a2a-executor-picker">
|
|
4258
|
+
<label class="a2a-executor-picker__option">
|
|
4259
|
+
<input type="radio" name="executor" value="codex" ${pickedExecutor === "codex" ? "checked" : ""} />
|
|
4260
|
+
<span>${escapeHtml(L("a2a.executor.codex"))}</span>
|
|
4261
|
+
</label>
|
|
4262
|
+
<label class="a2a-executor-picker__option">
|
|
4263
|
+
<input type="radio" name="executor" value="claude" ${pickedExecutor === "claude" ? "checked" : ""} />
|
|
4264
|
+
<span>${escapeHtml(L("a2a.executor.claude"))}</span>
|
|
4265
|
+
</label>
|
|
4266
|
+
</div>
|
|
4267
|
+
</div>
|
|
4268
|
+
`
|
|
4269
|
+
: "";
|
|
4270
|
+
|
|
4091
4271
|
const buttons = enabled
|
|
4092
4272
|
? `
|
|
4093
4273
|
<div class="actions actions--stack${options.mobile ? " actions--sticky" : ""}">
|
|
@@ -4111,6 +4291,13 @@ function renderA2ATaskDetail(detail, options = {}) {
|
|
|
4111
4291
|
<label class="field-label">${escapeHtml(L("a2a.task.instruction"))}</label>
|
|
4112
4292
|
<textarea name="instruction" class="reply-composer__textarea" rows="6" ${enabled ? "" : "readonly"}>${escapeHtml(instruction)}</textarea>
|
|
4113
4293
|
</div>
|
|
4294
|
+
${executorPicker}
|
|
4295
|
+
${!enabled && detail.messageText ? `
|
|
4296
|
+
<div class="reply-composer__instruction">
|
|
4297
|
+
<label class="field-label">${escapeHtml(L("a2a.task.response"))}</label>
|
|
4298
|
+
<pre class="a2a-task-response">${escapeHtml(detail.messageText)}</pre>
|
|
4299
|
+
</div>
|
|
4300
|
+
` : ""}
|
|
4114
4301
|
${buttonsWrapped}
|
|
4115
4302
|
</form>
|
|
4116
4303
|
</section>
|
|
@@ -4168,7 +4355,7 @@ function renderThreadShareDetail(detail, options = {}) {
|
|
|
4168
4355
|
}
|
|
4169
4356
|
|
|
4170
4357
|
function renderCompletionReplyComposer(detail, options = {}) {
|
|
4171
|
-
if (detail.kind !== "completion" || detail.reply?.enabled !== true) {
|
|
4358
|
+
if ((detail.kind !== "completion" && detail.kind !== "assistant_final") || detail.reply?.enabled !== true) {
|
|
4172
4359
|
return "";
|
|
4173
4360
|
}
|
|
4174
4361
|
|
|
@@ -4238,7 +4425,7 @@ function renderCompletionReplyComposer(detail, options = {}) {
|
|
|
4238
4425
|
</div>
|
|
4239
4426
|
`
|
|
4240
4427
|
: `
|
|
4241
|
-
<form class="reply-composer__form" data-completion-reply-form data-token="${escapeHtml(detail.token)}" data-provider="${escapeHtml(normalizeProviderClient(detail?.provider))}">
|
|
4428
|
+
<form class="reply-composer__form" data-completion-reply-form data-token="${escapeHtml(detail.token)}" data-provider="${escapeHtml(normalizeProviderClient(detail?.provider))}" data-reply-kind="${escapeHtml(detail.kind)}">
|
|
4242
4429
|
<label class="field reply-field">
|
|
4243
4430
|
<span class="field-label">${escapeHtml(L("reply.fieldLabel"))}</span>
|
|
4244
4431
|
<div class="reply-field__shell">
|
|
@@ -4816,6 +5003,11 @@ function bindShellInteractions() {
|
|
|
4816
5003
|
return;
|
|
4817
5004
|
}
|
|
4818
5005
|
state.providerFilter = next;
|
|
5006
|
+
// Reset thread/kind filters — each provider has its own set of valid options
|
|
5007
|
+
state.timelineThreadFilter = "all";
|
|
5008
|
+
state.timelineKindFilter = "all";
|
|
5009
|
+
state.timelineKindFilterOpen = false;
|
|
5010
|
+
state.completedThreadFilter = "all";
|
|
4819
5011
|
alignCurrentItemToVisibleEntries();
|
|
4820
5012
|
await renderShell();
|
|
4821
5013
|
});
|
|
@@ -5161,6 +5353,46 @@ function bindShellInteractions() {
|
|
|
5161
5353
|
});
|
|
5162
5354
|
}
|
|
5163
5355
|
|
|
5356
|
+
for (const checkbox of document.querySelectorAll("[data-a2a-public-checkbox]")) {
|
|
5357
|
+
checkbox.addEventListener("change", async () => {
|
|
5358
|
+
const next = checkbox.checked === true;
|
|
5359
|
+
try {
|
|
5360
|
+
await apiPost("/api/a2a/public-tasks", { accept: next });
|
|
5361
|
+
state.a2aRelayStatus = await apiGet("/api/a2a/relay-status");
|
|
5362
|
+
} catch (error) {
|
|
5363
|
+
state.pushError = error.message || String(error);
|
|
5364
|
+
}
|
|
5365
|
+
await renderShell();
|
|
5366
|
+
});
|
|
5367
|
+
}
|
|
5368
|
+
|
|
5369
|
+
for (const radio of document.querySelectorAll("[data-a2a-executor-option] input[type='radio']")) {
|
|
5370
|
+
radio.addEventListener("change", async () => {
|
|
5371
|
+
if (!radio.checked) return;
|
|
5372
|
+
const preference = radio.value || "auto";
|
|
5373
|
+
try {
|
|
5374
|
+
await apiPost("/api/settings/a2a-executor", { preference });
|
|
5375
|
+
await refreshSession();
|
|
5376
|
+
} catch (error) {
|
|
5377
|
+
state.pushError = error.message || String(error);
|
|
5378
|
+
}
|
|
5379
|
+
await renderShell();
|
|
5380
|
+
});
|
|
5381
|
+
}
|
|
5382
|
+
|
|
5383
|
+
for (const btn of document.querySelectorAll("[data-moltbook-titles-more]")) {
|
|
5384
|
+
btn.addEventListener("click", async () => {
|
|
5385
|
+
state.moltbookRecentTitlesExpanded = (state.moltbookRecentTitlesExpanded || 5) + 5;
|
|
5386
|
+
await renderShell();
|
|
5387
|
+
});
|
|
5388
|
+
}
|
|
5389
|
+
for (const btn of document.querySelectorAll("[data-moltbook-titles-collapse]")) {
|
|
5390
|
+
btn.addEventListener("click", async () => {
|
|
5391
|
+
state.moltbookRecentTitlesExpanded = 0;
|
|
5392
|
+
await renderShell();
|
|
5393
|
+
});
|
|
5394
|
+
}
|
|
5395
|
+
|
|
5164
5396
|
for (const button of document.querySelectorAll("[data-locale-option]")) {
|
|
5165
5397
|
button.addEventListener("click", async () => {
|
|
5166
5398
|
state.pushError = "";
|
|
@@ -5294,6 +5526,12 @@ function bindShellInteractions() {
|
|
|
5294
5526
|
});
|
|
5295
5527
|
}
|
|
5296
5528
|
|
|
5529
|
+
for (const radio of document.querySelectorAll(".a2a-executor-picker input[type='radio']")) {
|
|
5530
|
+
radio.addEventListener("change", () => {
|
|
5531
|
+
if (radio.checked) state.a2aTaskExecutorPick = radio.value;
|
|
5532
|
+
});
|
|
5533
|
+
}
|
|
5534
|
+
|
|
5297
5535
|
const a2aTaskForm = document.querySelector("[data-a2a-task-form]");
|
|
5298
5536
|
if (a2aTaskForm) {
|
|
5299
5537
|
const token = a2aTaskForm.dataset.token || "";
|
|
@@ -5322,12 +5560,16 @@ function bindShellInteractions() {
|
|
|
5322
5560
|
});
|
|
5323
5561
|
if (textarea) textarea.readOnly = true;
|
|
5324
5562
|
const instruction = normalizeClientText(new FormData(a2aTaskForm).get("instruction"));
|
|
5563
|
+
const executorRadio = a2aTaskForm.querySelector("input[name='executor']:checked");
|
|
5564
|
+
const executor = executorRadio ? executorRadio.value : "";
|
|
5325
5565
|
try {
|
|
5566
|
+
const decisionBody = { action: submittedAction, instruction };
|
|
5567
|
+
if (executor) decisionBody.executor = executor;
|
|
5326
5568
|
const res = await fetch(`/api/items/a2a-task/${encodeURIComponent(token)}/decision`, {
|
|
5327
5569
|
method: "POST",
|
|
5328
5570
|
headers: { "content-type": "application/json" },
|
|
5329
5571
|
credentials: "same-origin",
|
|
5330
|
-
body: JSON.stringify(
|
|
5572
|
+
body: JSON.stringify(decisionBody),
|
|
5331
5573
|
});
|
|
5332
5574
|
if (!res.ok) {
|
|
5333
5575
|
const errBody = await res.json().catch(() => ({}));
|
|
@@ -5482,7 +5724,8 @@ function bindShellInteractions() {
|
|
|
5482
5724
|
requestBody.append("image", attachment.file, attachment.name || attachment.file.name);
|
|
5483
5725
|
}
|
|
5484
5726
|
}
|
|
5485
|
-
|
|
5727
|
+
const replyKind = replyForm.dataset.replyKind || "completion";
|
|
5728
|
+
await apiPost(`/api/items/${encodeURIComponent(replyKind)}/${encodeURIComponent(token)}/reply`, requestBody);
|
|
5486
5729
|
setCompletionReplyDraft(token, {
|
|
5487
5730
|
text: "",
|
|
5488
5731
|
sentText: text,
|
|
@@ -5880,10 +6123,10 @@ function inboxSubtabForItemKind(kind, sourceSubtab = "") {
|
|
|
5880
6123
|
if (normalizeClientText(sourceSubtab || "") === "completed") {
|
|
5881
6124
|
return "completed";
|
|
5882
6125
|
}
|
|
5883
|
-
return kind === "completion" ? "completed" : "pending";
|
|
6126
|
+
return kind === "completion" || kind === "assistant_final" ? "completed" : "pending";
|
|
5884
6127
|
}
|
|
5885
6128
|
|
|
5886
|
-
function kindMeta(kind) {
|
|
6129
|
+
function kindMeta(kind, item) {
|
|
5887
6130
|
switch (kind) {
|
|
5888
6131
|
case "user_message":
|
|
5889
6132
|
return { label: L("common.userMessage"), tone: "neutral", icon: "user-message" };
|
|
@@ -5905,10 +6148,17 @@ function kindMeta(kind) {
|
|
|
5905
6148
|
case "file_event":
|
|
5906
6149
|
return { label: L("common.fileEvent"), tone: "neutral", icon: "file-event" };
|
|
5907
6150
|
case "moltbook_reply":
|
|
6151
|
+
return { label: L("common.moltbookReply"), tone: "neutral", icon: "moltbook-comment" };
|
|
5908
6152
|
case "moltbook_draft":
|
|
5909
|
-
return
|
|
6153
|
+
return item?.draftType === "reply"
|
|
6154
|
+
? { label: L("common.moltbookDraftReply"), tone: "neutral", icon: "moltbook-reply" }
|
|
6155
|
+
: { label: L("common.moltbookDraft"), tone: "neutral", icon: "moltbook-draft" };
|
|
5910
6156
|
case "thread_share":
|
|
5911
6157
|
return { label: L("common.threadShare"), tone: "neutral", icon: "link" };
|
|
6158
|
+
case "a2a_task":
|
|
6159
|
+
return { label: L("common.a2aTaskRequest"), tone: "neutral", icon: "item" };
|
|
6160
|
+
case "a2a_task_result":
|
|
6161
|
+
return { label: L("common.a2aTaskResult"), tone: "completion", icon: "completion-item" };
|
|
5912
6162
|
default:
|
|
5913
6163
|
return { label: L("common.item"), tone: "neutral", icon: "item" };
|
|
5914
6164
|
}
|
|
@@ -6263,6 +6513,8 @@ function renderIcon(name) {
|
|
|
6263
6513
|
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="m9.5 12.5 5.9-5.9a3 3 0 1 1 4.2 4.2l-7.7 7.7a5 5 0 1 1-7.1-7.1l8.1-8.1"/></svg>`;
|
|
6264
6514
|
case "moltbook-draft":
|
|
6265
6515
|
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M15.2 3.8 20.2 8.8 8.5 20.5 3.5 20.5 3.5 15.5Z"/><path d="M12.5 6.5l5 5"/></svg>`;
|
|
6516
|
+
case "moltbook-reply":
|
|
6517
|
+
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M9 4 4 9l5 5"/><path d="M4 9h11a4 4 0 0 1 4 4v3"/></svg>`;
|
|
6266
6518
|
case "moltbook-comment":
|
|
6267
6519
|
return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 5.5h15a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H11l-4 3.5v-3.5H4.5a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2Z"/><path d="M8 10h8"/><path d="M8 13h5"/></svg>`;
|
|
6268
6520
|
case "filter":
|