relay-companion 0.1.410 → 0.1.412
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/npm-shrinkwrap.json +2 -2
- package/overlay/inbox.html +39 -17
- package/overlay/main.cjs +23 -0
- package/package.json +1 -1
- package/src/product-features.cjs +7 -0
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relay-companion",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.412",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "relay-companion",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.412",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@anthropic-ai/claude-agent-sdk": "0.3.195",
|
package/overlay/inbox.html
CHANGED
|
@@ -2173,8 +2173,17 @@
|
|
|
2173
2173
|
.th-mention-copy { display:flex; flex-direction:column; min-width:0; }
|
|
2174
2174
|
.th-mention-name { font-weight:650; }
|
|
2175
2175
|
.th-mention-note { color:var(--muted); font-size:10.5px; }
|
|
2176
|
-
|
|
2177
|
-
|
|
2176
|
+
/* Chat-agent defaults stay compact without forcing four controls onto one
|
|
2177
|
+
line. Identity owns the first line; model and thinking share a labelled,
|
|
2178
|
+
responsive grid below it. */
|
|
2179
|
+
.sv-agent-defaults .sv-open-row { display:block; min-height:0; padding:11px 12px 12px; }
|
|
2180
|
+
.sv-agent-identity { display:flex; align-items:center; gap:11px; min-width:0; }
|
|
2181
|
+
.sv-agent-fields { display:grid; grid-template-columns:minmax(0,1.35fr) minmax(0,1fr); gap:8px; margin:10px 0 0 39px; }
|
|
2182
|
+
.sv-agent-field { display:block; min-width:0; }
|
|
2183
|
+
.sv-agent-field-label { display:block; margin:0 0 4px 1px; color:var(--muted-2); font:10px/1.25 var(--sans); }
|
|
2184
|
+
.sv-agent-select { width:100%; min-width:0; height:34px; padding:0 8px; border:1px solid var(--edge); border-radius:9px;
|
|
2185
|
+
background:var(--bg); color:var(--ink); font:11.5px var(--sans); cursor:pointer; }
|
|
2186
|
+
.sv-agent-select:focus-visible { outline:none; box-shadow:var(--focus-ring); }
|
|
2178
2187
|
/* The chosen Relay is attached to the existing composer as its top face.
|
|
2179
2188
|
This is one composer with context, not a Slack-style child conversation. */
|
|
2180
2189
|
.qr.th-qr.replying { gap:0; }
|
|
@@ -4268,7 +4277,7 @@
|
|
|
4268
4277
|
|
|
4269
4278
|
// ---------- state ----------
|
|
4270
4279
|
let activeView = "relays";
|
|
4271
|
-
let payload = { account: { paired: false }, ui: { canDismiss: true, reopenSurface: "the Relay icon", setupTutorialPending:false }, features: { requests:false, cowork:false, environment:"production" }, relays: [], sent: [], requests: [], contacts: [], chats: [], slackChats: [] };
|
|
4280
|
+
let payload = { account: { paired: false }, ui: { canDismiss: true, reopenSurface: "the Relay icon", setupTutorialPending:false }, features: { requests:false, slack:false, peopleMentions:true, agentMentions:false, cowork:false, environment:"production" }, relays: [], sent: [], requests: [], contacts: [], chats: [], slackChats: [] };
|
|
4272
4281
|
const canonicalChatDetails = new Map();
|
|
4273
4282
|
const canonicalChatFingerprints = new Map();
|
|
4274
4283
|
const canonicalChatDetailProjectionKeys = new Map();
|
|
@@ -4311,6 +4320,7 @@
|
|
|
4311
4320
|
return `${conversationSurface(source)}:${chatId}`;
|
|
4312
4321
|
}
|
|
4313
4322
|
function slackMessagesVisible(room, source = threadsSource) {
|
|
4323
|
+
if (payload.features?.slack !== true) return false;
|
|
4314
4324
|
if (!isSlackIntegratedRoom(room)) return false;
|
|
4315
4325
|
const key = slackVisibilityKey(room, source);
|
|
4316
4326
|
return slackVisibilityOverrides.has(key)
|
|
@@ -4405,7 +4415,7 @@
|
|
|
4405
4415
|
return changed;
|
|
4406
4416
|
}
|
|
4407
4417
|
function syncSlackVisibilityButton(room = threadDetailId ? chatRoomForThread(threadDetailId) : null) {
|
|
4408
|
-
const linked = isSlackIntegratedRoom(room);
|
|
4418
|
+
const linked = payload.features?.slack === true && isSlackIntegratedRoom(room);
|
|
4409
4419
|
thSlackVisibilityEl.classList.toggle("hidden", !linked);
|
|
4410
4420
|
if (!linked) return;
|
|
4411
4421
|
const visible = slackMessagesVisible(room);
|
|
@@ -11551,7 +11561,7 @@
|
|
|
11551
11561
|
// recipients only; the owned Claude and Codex options remain available
|
|
11552
11562
|
// in every developer composer below.
|
|
11553
11563
|
const participantNames = new Set();
|
|
11554
|
-
if (thread.isGroup) {
|
|
11564
|
+
if (payload.features?.peopleMentions === true && thread.isGroup) {
|
|
11555
11565
|
const group = groupsList.find((candidate) => String(candidate.id || "") === String(thread.groupId || ""))
|
|
11556
11566
|
|| groupsList.find((candidate) => normalizedPartyName(candidate.name) === normalizedPartyName(thread.groupName));
|
|
11557
11567
|
// The current roster decides who can be mentioned. Historical sender
|
|
@@ -11562,7 +11572,7 @@
|
|
|
11562
11572
|
}
|
|
11563
11573
|
}
|
|
11564
11574
|
const mentionOptions = [
|
|
11565
|
-
...(payload.features?.
|
|
11575
|
+
...(payload.features?.agentMentions === true ? [
|
|
11566
11576
|
{ token:"Claude", name:"@Claude", note:"Run Claude on my computer", agent:true },
|
|
11567
11577
|
{ token:"Codex", name:"@Codex", note:"Run Codex on my computer", agent:true },
|
|
11568
11578
|
] : []),
|
|
@@ -11719,7 +11729,7 @@
|
|
|
11719
11729
|
attachments,
|
|
11720
11730
|
preview: "",
|
|
11721
11731
|
});
|
|
11722
|
-
const agentMentions = payload.features?.
|
|
11732
|
+
const agentMentions = payload.features?.agentMentions === true ? agentMentionSpans(text) : [];
|
|
11723
11733
|
const invokedProviders = agentMentions.map((mention) => mention.provider);
|
|
11724
11734
|
for (const [index, provider] of invokedProviders.entries()) {
|
|
11725
11735
|
optimisticChatReplies.set(`${idempotencyKey}:agent:${provider}`, {
|
|
@@ -12161,6 +12171,7 @@
|
|
|
12161
12171
|
}
|
|
12162
12172
|
|
|
12163
12173
|
async function refreshSlackConnection({ preserveWaiting = true } = {}) {
|
|
12174
|
+
if (payload.features?.slack !== true) return;
|
|
12164
12175
|
if (!window.relay.slackConnection) return;
|
|
12165
12176
|
if (!rendererSurfaceActive()) {
|
|
12166
12177
|
slackConnectionRefreshDeferred = true;
|
|
@@ -12484,9 +12495,12 @@
|
|
|
12484
12495
|
<div class="sv-open-intro">Defaults for Work sessions that run on this computer.</div>
|
|
12485
12496
|
<div class="sv-open-list">${rows.map((row) => {
|
|
12486
12497
|
const current = chatAgentPreferences[row.key] || {};
|
|
12487
|
-
return `<div class="sv-open-row"
|
|
12488
|
-
<
|
|
12489
|
-
<
|
|
12498
|
+
return `<div class="sv-open-row">
|
|
12499
|
+
<span class="sv-agent-identity"><img class="sv-open-logo" src="${row.logo}" alt="" /><span class="sv-open-copy"><span class="sv-open-name">${row.label}</span></span></span>
|
|
12500
|
+
<span class="sv-agent-fields">
|
|
12501
|
+
<label class="sv-agent-field"><span class="sv-agent-field-label">Model</span><select class="sv-agent-select" data-chat-agent-model="${row.key}" aria-label="${row.label} model">${row.models.map(([value,label]) => `<option value="${value}"${current.model === value ? " selected" : ""}>${label}</option>`).join("")}</select></label>
|
|
12502
|
+
<label class="sv-agent-field"><span class="sv-agent-field-label">Thinking</span><select class="sv-agent-select" data-chat-agent-effort="${row.key}" aria-label="${row.label} thinking">${row.efforts.map((value) => `<option value="${value}"${current.effort === value ? " selected" : ""}>${value}</option>`).join("")}</select></label>
|
|
12503
|
+
</span>
|
|
12490
12504
|
</div>`;
|
|
12491
12505
|
}).join("")}</div>
|
|
12492
12506
|
${chatAgentPreferencesError ? `<div class="row-err sv-err">${esc(chatAgentPreferencesError)}</div>` : ""}
|
|
@@ -12524,6 +12538,7 @@
|
|
|
12524
12538
|
</section>`;
|
|
12525
12539
|
}
|
|
12526
12540
|
function slackSettingsHtml(info) {
|
|
12541
|
+
if (payload.features?.slack !== true) return "";
|
|
12527
12542
|
if (!info?.paired || !window.relay.slackConnection) return "";
|
|
12528
12543
|
const connection = slackConnectionInfo;
|
|
12529
12544
|
const state = connection?.state || "disconnected";
|
|
@@ -13154,6 +13169,7 @@
|
|
|
13154
13169
|
renderSettings();
|
|
13155
13170
|
})();
|
|
13156
13171
|
const slackTask = (async () => {
|
|
13172
|
+
if (payload.features?.slack !== true) return;
|
|
13157
13173
|
if (seq !== settingsLoadSeq) return;
|
|
13158
13174
|
await refreshSlackConnection({ preserveWaiting:true });
|
|
13159
13175
|
})();
|
|
@@ -13212,14 +13228,14 @@
|
|
|
13212
13228
|
window.addEventListener("focus", () => {
|
|
13213
13229
|
if (activeView === "settings") {
|
|
13214
13230
|
loadSettings();
|
|
13215
|
-
} else if (activeView === "slack") {
|
|
13231
|
+
} else if (activeView === "slack" && payload.features?.slack === true) {
|
|
13216
13232
|
refreshSlackConnection({ preserveWaiting:true });
|
|
13217
13233
|
}
|
|
13218
13234
|
});
|
|
13219
13235
|
document.addEventListener("visibilitychange", () => {
|
|
13220
13236
|
if (document.visibilityState === "visible") {
|
|
13221
13237
|
if (activeView === "settings") loadSettings();
|
|
13222
|
-
else if (activeView === "slack") refreshSlackConnection({ preserveWaiting:true });
|
|
13238
|
+
else if (activeView === "slack" && payload.features?.slack === true) refreshSlackConnection({ preserveWaiting:true });
|
|
13223
13239
|
}
|
|
13224
13240
|
});
|
|
13225
13241
|
// Slack OAuth completes in the browser, not in this renderer. Focus and
|
|
@@ -13228,7 +13244,7 @@
|
|
|
13228
13244
|
// a manual refresh. The tab and Settings card share the same lightweight
|
|
13229
13245
|
// status call; all other Settings checks retain their focus/entry cadence.
|
|
13230
13246
|
setInterval(() => {
|
|
13231
|
-
if ((activeView === "settings" || activeView === "slack") && rendererSurfaceActive()) {
|
|
13247
|
+
if (payload.features?.slack === true && (activeView === "settings" || activeView === "slack") && rendererSurfaceActive()) {
|
|
13232
13248
|
refreshSlackConnection({ preserveWaiting:true });
|
|
13233
13249
|
}
|
|
13234
13250
|
}, SLACK_CONNECTION_POLL_MS);
|
|
@@ -13699,7 +13715,9 @@
|
|
|
13699
13715
|
: activeView;
|
|
13700
13716
|
for (const tab of tabEls) {
|
|
13701
13717
|
const view = tab.getAttribute("data-view");
|
|
13702
|
-
tab.classList.toggle("gone",
|
|
13718
|
+
tab.classList.toggle("gone",
|
|
13719
|
+
(view === "tasks" && payload.features?.requests !== true)
|
|
13720
|
+
|| (view === "slack" && payload.features?.slack !== true));
|
|
13703
13721
|
tab.classList.toggle("active", view === tabView);
|
|
13704
13722
|
}
|
|
13705
13723
|
}
|
|
@@ -13732,7 +13750,7 @@
|
|
|
13732
13750
|
} else {
|
|
13733
13751
|
resetSignOutArm(); // leaving the view disarms the confirm state
|
|
13734
13752
|
}
|
|
13735
|
-
if (activeView === "slack" && viewChanged) {
|
|
13753
|
+
if (activeView === "slack" && payload.features?.slack === true && viewChanged) {
|
|
13736
13754
|
refreshSlackConnection({ preserveWaiting:true });
|
|
13737
13755
|
}
|
|
13738
13756
|
// A room owns its sent-cache refresh in hydrateThreadEntry(), where the
|
|
@@ -13866,8 +13884,8 @@
|
|
|
13866
13884
|
const unread = relayUnreadIds.size;
|
|
13867
13885
|
setCount(unread);
|
|
13868
13886
|
setBadge(relaysBadgeEl, unread);
|
|
13869
|
-
setBadge(slackBadgeEl, (payload.slackChats || []).reduce(
|
|
13870
|
-
(sum, chat) => sum + Math.max(0, Number(chat?.unreadCount || 0)), 0));
|
|
13887
|
+
setBadge(slackBadgeEl, payload.features?.slack === true ? (payload.slackChats || []).reduce(
|
|
13888
|
+
(sum, chat) => sum + Math.max(0, Number(chat?.unreadCount || 0)), 0) : 0);
|
|
13871
13889
|
markAllReadEl.classList.toggle("gone", activeView !== "relays" || unread === 0);
|
|
13872
13890
|
markAllReadEl.disabled = unread === 0;
|
|
13873
13891
|
setBadge(tasksBadgeEl, requestsUnreadCount());
|
|
@@ -13982,6 +14000,10 @@
|
|
|
13982
14000
|
if (payload.features?.requests !== true && ["tasks", "requestDetail"].includes(activeView)) {
|
|
13983
14001
|
activeView = "relays";
|
|
13984
14002
|
}
|
|
14003
|
+
if (payload.features?.slack !== true && (activeView === "slack" || threadsSource === "slack")) {
|
|
14004
|
+
activeView = "relays";
|
|
14005
|
+
threadsSource = "relay";
|
|
14006
|
+
}
|
|
13985
14007
|
// Delivery can happen after navigation. If its room is the visible Chat
|
|
13986
14008
|
// transcript, receipt of the full payload is itself a read event.
|
|
13987
14009
|
if (paintSurface) readVisibleChatRoom();
|
package/overlay/main.cjs
CHANGED
|
@@ -874,7 +874,9 @@ async function refreshAccountProductFeatures() {
|
|
|
874
874
|
TASK_FEATURES_ALLOWED = next.requests;
|
|
875
875
|
if (changed) {
|
|
876
876
|
tasksLoadedOnce = null;
|
|
877
|
+
canonicalChatsLoadedOnce = null;
|
|
877
878
|
await refreshTasks();
|
|
879
|
+
await refreshCanonicalChats();
|
|
878
880
|
await pushInbox(true);
|
|
879
881
|
}
|
|
880
882
|
return changed;
|
|
@@ -1714,6 +1716,12 @@ function isSlackLinkedChat(chat) {
|
|
|
1714
1716
|
));
|
|
1715
1717
|
}
|
|
1716
1718
|
async function refreshCanonicalChats() {
|
|
1719
|
+
if (PRODUCT_FEATURES.slack !== true) {
|
|
1720
|
+
canonicalChatsCache = [];
|
|
1721
|
+
slackChatsCache = [];
|
|
1722
|
+
canonicalChatsFingerprint = canonicalChatsFingerprintOf([], []);
|
|
1723
|
+
return { relay: canonicalChatsCache, slack: slackChatsCache };
|
|
1724
|
+
}
|
|
1717
1725
|
if (!deviceToken()) return { relay: canonicalChatsCache, slack: slackChatsCache };
|
|
1718
1726
|
try {
|
|
1719
1727
|
const client = await relayClient();
|
|
@@ -7319,6 +7327,9 @@ ipcMain.handle("relay:canonicalChat", async (event, input) => {
|
|
|
7319
7327
|
}
|
|
7320
7328
|
const { chatId: id, surface, includeSlack } = canonicalChatIpcInput(input);
|
|
7321
7329
|
if (!id) return { ok: false, error: "Missing channel id." };
|
|
7330
|
+
if (PRODUCT_FEATURES.slack !== true && (surface === "slack" || includeSlack)) {
|
|
7331
|
+
return { ok: false, error: "Slack is available only to Relay developer accounts on dev." };
|
|
7332
|
+
}
|
|
7322
7333
|
try {
|
|
7323
7334
|
const client = await relayClient();
|
|
7324
7335
|
const chat = await client.chat(id, { surface, includeSlack });
|
|
@@ -7333,6 +7344,9 @@ ipcMain.handle("relay:canonicalChatRead", async (event, input) => {
|
|
|
7333
7344
|
}
|
|
7334
7345
|
const { chatId: id, surface, includeSlack } = canonicalChatIpcInput(input);
|
|
7335
7346
|
if (!id) return { ok: false, error: "Missing channel id." };
|
|
7347
|
+
if (PRODUCT_FEATURES.slack !== true && (surface === "slack" || includeSlack)) {
|
|
7348
|
+
return { ok: false, error: "Slack is available only to Relay developer accounts on dev." };
|
|
7349
|
+
}
|
|
7336
7350
|
if (!chatReadPresenceIsAvailable(win)) {
|
|
7337
7351
|
deferChatRead();
|
|
7338
7352
|
return { ok: false, deferred: true };
|
|
@@ -7976,10 +7990,16 @@ ipcMain.handle("relay:contactsSearch", (_e, q) => groupCall((c) => c.searchConta
|
|
|
7976
7990
|
// Settings tab: account card + the sign-out / switch-account lifecycle.
|
|
7977
7991
|
ipcMain.handle("relay:accountInfo", () => accountInfo());
|
|
7978
7992
|
ipcMain.handle("relay:slackConnection", async () => {
|
|
7993
|
+
if (PRODUCT_FEATURES.slack !== true) {
|
|
7994
|
+
return { ok: false, error: "Slack is available only to Relay developer accounts on dev." };
|
|
7995
|
+
}
|
|
7979
7996
|
try { return { ok: true, connection: await (await relayClient()).slackConnection() }; }
|
|
7980
7997
|
catch (error) { return { ok: false, error: (error && error.message) || String(error) }; }
|
|
7981
7998
|
});
|
|
7982
7999
|
ipcMain.handle("relay:slackConnect", async (_event, input = {}) => {
|
|
8000
|
+
if (PRODUCT_FEATURES.slack !== true) {
|
|
8001
|
+
return { ok: false, error: "Slack is available only to Relay developer accounts on dev." };
|
|
8002
|
+
}
|
|
7983
8003
|
try {
|
|
7984
8004
|
const client = await relayClient();
|
|
7985
8005
|
const result = input?.reconnect
|
|
@@ -7997,6 +8017,9 @@ ipcMain.handle("relay:slackConnect", async (_event, input = {}) => {
|
|
|
7997
8017
|
}
|
|
7998
8018
|
});
|
|
7999
8019
|
ipcMain.handle("relay:slackDisconnect", async () => {
|
|
8020
|
+
if (PRODUCT_FEATURES.slack !== true) {
|
|
8021
|
+
return { ok: false, error: "Slack is available only to Relay developer accounts on dev." };
|
|
8022
|
+
}
|
|
8000
8023
|
try { return { ok: true, result: await (await relayClient()).disconnectSlack() }; }
|
|
8001
8024
|
catch (error) { return { ok: false, error: (error && error.message) || String(error) }; }
|
|
8002
8025
|
});
|
package/package.json
CHANGED
package/src/product-features.cjs
CHANGED
|
@@ -33,6 +33,13 @@ function productFeatures(options = {}) {
|
|
|
33
33
|
environment,
|
|
34
34
|
developer,
|
|
35
35
|
requests: developer,
|
|
36
|
+
// Slack is an internal proving surface alongside Tasks. Staging and
|
|
37
|
+
// production exercise the customer product, even for developer accounts.
|
|
38
|
+
slack: developer,
|
|
39
|
+
// Human mentions are ordinary conversation affordances. Agent mentions
|
|
40
|
+
// start local runs, so they retain the same boundary as Tasks.
|
|
41
|
+
peopleMentions: true,
|
|
42
|
+
agentMentions: developer,
|
|
36
43
|
// Cowork is intentionally unavailable. Do not add an override here: its
|
|
37
44
|
// former transport inspected Claude Desktop process/session credentials.
|
|
38
45
|
cowork: false,
|