relay-companion 0.1.396 → 0.1.398
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 +73 -25
- package/overlay/main.cjs +14 -3
- package/package.json +1 -1
- package/src/task-daemon.js +11 -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.398",
|
|
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.398",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@anthropic-ai/claude-agent-sdk": "0.3.195",
|
package/overlay/inbox.html
CHANGED
|
@@ -2245,9 +2245,34 @@
|
|
|
2245
2245
|
.sv-provider-list { overflow:hidden; border-radius:var(--r-3); background:var(--raised); box-shadow:inset 0 0 0 1px var(--edge); }
|
|
2246
2246
|
.sv-provider { padding:12px; }
|
|
2247
2247
|
.sv-provider + .sv-provider { border-top:1px solid var(--edge); }
|
|
2248
|
-
.sv-provider-list.sv-agent-provider-list {
|
|
2249
|
-
.sv-agent-provider-list .sv-provider {
|
|
2250
|
-
.sv-agent-provider-list .sv-provider + .sv-provider { border-top:
|
|
2248
|
+
.sv-provider-list.sv-agent-provider-list { background:var(--raised); }
|
|
2249
|
+
.sv-agent-provider-list .sv-provider { padding:0; }
|
|
2250
|
+
.sv-agent-provider-list .sv-provider + .sv-provider { border-top:1px solid var(--edge); }
|
|
2251
|
+
.sv-agent-provider-toggle {
|
|
2252
|
+
appearance:none; width:100%; min-height:64px; padding:12px; border:0; background:transparent; color:inherit;
|
|
2253
|
+
display:grid; grid-template-columns:28px minmax(0,1fr) auto 14px; align-items:center; gap:10px;
|
|
2254
|
+
font-family:var(--sans); text-align:left; cursor:pointer; transition:background-color var(--t-fast) var(--settle);
|
|
2255
|
+
}
|
|
2256
|
+
.sv-agent-provider-toggle:hover { background:var(--wash); }
|
|
2257
|
+
.sv-agent-provider-logo { width:28px; height:28px; border-radius:7px; object-fit:contain; }
|
|
2258
|
+
.sv-agent-provider-summary { min-width:0; display:flex; flex-direction:column; }
|
|
2259
|
+
.sv-agent-provider-count { margin-top:3px; color:var(--muted-2); font-size:11px; line-height:1.2; }
|
|
2260
|
+
.sv-agent-provider-ready { display:inline-flex; align-items:center; gap:5px; color:var(--ok); font:600 10px/1 var(--sans); white-space:nowrap; }
|
|
2261
|
+
.sv-agent-provider-ready::before { content:""; width:5px; height:5px; border-radius:50%; background:currentColor; }
|
|
2262
|
+
.sv-agent-provider-state { color:var(--muted-2); font:600 10px/1 var(--sans); white-space:nowrap; }
|
|
2263
|
+
.sv-agent-provider-chev { color:var(--muted-3); transition:transform var(--t-base) var(--settle); }
|
|
2264
|
+
.sv-agent-provider-toggle[aria-expanded="true"] .sv-agent-provider-chev { transform:rotate(180deg); }
|
|
2265
|
+
.sv-agent-provider-detail { display:grid; grid-template-rows:0fr; transition:grid-template-rows var(--t-base) var(--soft); }
|
|
2266
|
+
.sv-agent-provider-detail.open { grid-template-rows:1fr; }
|
|
2267
|
+
.sv-agent-provider-detail-inner { min-height:0; overflow:hidden; }
|
|
2268
|
+
.sv-agent-provider-detail-body { margin:0 12px 13px 50px; padding-top:11px; border-top:1px solid var(--hair); }
|
|
2269
|
+
.sv-agent-provider-note { margin-bottom:11px; color:var(--muted-2); font-size:10.5px; line-height:1.4; }
|
|
2270
|
+
.sv-agent-provider-group + .sv-agent-provider-group { margin-top:12px; }
|
|
2271
|
+
.sv-agent-provider-group-head { display:flex; align-items:baseline; justify-content:space-between; gap:10px; margin-bottom:7px; color:var(--muted-2); font:600 10px/1.25 var(--sans); }
|
|
2272
|
+
.sv-agent-provider-integrations { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); gap:7px 12px; margin:0; padding:0; list-style:none; }
|
|
2273
|
+
.sv-agent-provider-integrations li { min-width:0; overflow:hidden; color:var(--muted); font:500 10px/1.25 var(--sans); text-overflow:ellipsis; white-space:nowrap; }
|
|
2274
|
+
.sv-agent-provider-integrations li::before { content:"·"; margin-right:5px; color:var(--muted-3); }
|
|
2275
|
+
.sv-agent-provider-actions { display:flex; gap:7px; justify-content:flex-end; margin-top:12px; }
|
|
2251
2276
|
.sv-provider-top { display:flex; align-items:center; gap:9px; }
|
|
2252
2277
|
.sv-provider-copy { min-width:0; flex:1 1 auto; display:flex; flex-direction:column; }
|
|
2253
2278
|
.sv-provider-name { font-size:13px; font-weight:600; color:var(--ink); }
|
|
@@ -2266,7 +2291,7 @@
|
|
|
2266
2291
|
.sv-provider-btn.primary { border-color:color-mix(in srgb, var(--accent) 35%, var(--edge)); background:var(--accent-soft); color:var(--accent); }
|
|
2267
2292
|
.sv-provider-btn:hover { color:var(--ink); border-color:var(--edge-strong); }
|
|
2268
2293
|
.sv-provider-btn:disabled { opacity:.4; cursor:default; }
|
|
2269
|
-
@media (prefers-reduced-motion:reduce) { .sv-provider-dot.busy { animation:none; } }
|
|
2294
|
+
@media (prefers-reduced-motion:reduce) { .sv-provider-dot.busy { animation:none; } .sv-agent-provider-detail, .sv-agent-provider-chev { transition:none; } }
|
|
2270
2295
|
|
|
2271
2296
|
/* ---- Quiet mode: the two durable on/off preferences ----
|
|
2272
2297
|
A real switch, not the Enable/Disable label-flip the provider rows use: these
|
|
@@ -11183,6 +11208,7 @@
|
|
|
11183
11208
|
let chatAgentPreferencesError = "";
|
|
11184
11209
|
let providerAuthInfo = null; // safe status only; credentials stay in provider CLIs
|
|
11185
11210
|
let providerAuthBusy = "";
|
|
11211
|
+
let providerDetailsOpen = "";
|
|
11186
11212
|
let providerAuthNote = "";
|
|
11187
11213
|
let providerAuthError = "";
|
|
11188
11214
|
let providerInventoryRefreshing = false;
|
|
@@ -11337,7 +11363,6 @@
|
|
|
11337
11363
|
const item = providers[id] || { id, label: id === "claude" ? "Claude Code" : "Codex", enabled:true, installed:false, connected:false, detail:"Checking connection…", busy:false, authLoaded:false };
|
|
11338
11364
|
const authLoaded = item.authLoaded === true;
|
|
11339
11365
|
const busy = providerAuthBusy === id || item.busy;
|
|
11340
|
-
const state = !item.enabled ? "disabled" : busy ? "busy" : item.connected ? "connected" : "";
|
|
11341
11366
|
const requirement = item.requirement || (id === "claude" ? "Claude subscription required" : "ChatGPT subscription required");
|
|
11342
11367
|
const status = !item.enabled
|
|
11343
11368
|
? `${item.connected ? `${item.detail} · ` : ""}Disabled for Tasks`
|
|
@@ -11349,15 +11374,16 @@
|
|
|
11349
11374
|
const integrations = item.integrations && typeof item.integrations === "object" ? item.integrations : {};
|
|
11350
11375
|
const mcpServers = Array.isArray(integrations.mcpServers) ? integrations.mcpServers : [];
|
|
11351
11376
|
const apps = Array.isArray(integrations.apps) ? integrations.apps : [];
|
|
11377
|
+
const totalConnections = mcpServers.length + apps.length;
|
|
11352
11378
|
const integrationList = (label, values) => {
|
|
11353
11379
|
if (!values.length) return "";
|
|
11354
|
-
const
|
|
11380
|
+
const list = values.map((entry) => {
|
|
11355
11381
|
const name = typeof entry === "string" ? entry : entry && entry.name;
|
|
11356
|
-
return `<
|
|
11382
|
+
return `<li title="${esc(name || "")}">${esc(name || "")}</li>`;
|
|
11357
11383
|
}).join("");
|
|
11358
|
-
return `<div class="sv-
|
|
11359
|
-
<div class="sv-
|
|
11360
|
-
<
|
|
11384
|
+
return `<div class="sv-agent-provider-group">
|
|
11385
|
+
<div class="sv-agent-provider-group-head"><span>${esc(label)}</span><span>${esc(values.length)}</span></div>
|
|
11386
|
+
<ul class="sv-agent-provider-integrations">${list}</ul>
|
|
11361
11387
|
</div>`;
|
|
11362
11388
|
};
|
|
11363
11389
|
const enableLabel = item.enabled ? "Disable" : "Enable";
|
|
@@ -11370,26 +11396,35 @@
|
|
|
11370
11396
|
? (id === "claude" ? "Use Claude subscription" : "Use ChatGPT subscription")
|
|
11371
11397
|
: signInLabel;
|
|
11372
11398
|
const connectDisabled = busy || !authLoaded || !item.installed || !item.enabled || item.connected;
|
|
11399
|
+
const open = providerDetailsOpen === id;
|
|
11400
|
+
const healthy = item.enabled && item.connected && !busy;
|
|
11401
|
+
const logo = id === "claude" ? "claudeCodeMark.svg" : "codexMark.svg";
|
|
11402
|
+
const countLabel = totalConnections === 1 ? "1 connection" : `${totalConnections} connections`;
|
|
11403
|
+
const headerMeta = healthy ? countLabel : status;
|
|
11404
|
+
const headerState = healthy
|
|
11405
|
+
? '<span class="sv-agent-provider-ready">Ready</span>'
|
|
11406
|
+
: `<span class="sv-agent-provider-state">${esc(busy ? "Connecting" : item.enabled ? "Needs attention" : "Disabled")}</span>`;
|
|
11407
|
+
const detailNote = healthy ? "" : `<div class="sv-agent-provider-note">${esc(requirement)}${status ? ` · ${esc(status)}` : ""}</div>`;
|
|
11373
11408
|
return `<div class="sv-provider" data-provider-row="${id}">
|
|
11374
|
-
<
|
|
11375
|
-
<
|
|
11376
|
-
<span class="sv-provider-
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11409
|
+
<button class="sv-agent-provider-toggle" type="button" data-provider-details="${id}" aria-expanded="${open ? "true" : "false"}" aria-controls="svProviderDetail-${id}">
|
|
11410
|
+
<img class="sv-agent-provider-logo" src="${logo}" alt="" />
|
|
11411
|
+
<span class="sv-agent-provider-summary"><span class="sv-provider-name">${esc(item.label)}</span><span class="sv-agent-provider-count">${esc(headerMeta)}</span></span>
|
|
11412
|
+
${headerState}
|
|
11413
|
+
<svg class="sv-agent-provider-chev" width="12" height="7" viewBox="0 0 12 7" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M1.5 1.5L6 5.5L10.5 1.5"/></svg>
|
|
11414
|
+
</button>
|
|
11415
|
+
<div class="sv-agent-provider-detail${open ? " open" : ""}" id="svProviderDetail-${id}"><div class="sv-agent-provider-detail-inner"><div class="sv-agent-provider-detail-body">
|
|
11416
|
+
${detailNote}
|
|
11417
|
+
${integrationList(id === "claude" ? "MCPs and account connectors" : "Local MCPs", mcpServers)}
|
|
11418
|
+
${integrationList("Connected apps", apps)}
|
|
11419
|
+
<div class="sv-agent-provider-actions">
|
|
11420
|
+
${item.connected ? "" : `<button class="sv-provider-btn primary" type="button" data-provider-connect="${id}" ${connectDisabled ? "disabled" : ""}>${esc(connectLabel)}</button>`}
|
|
11421
|
+
<button class="sv-provider-btn" type="button" data-provider-enable="${id}" data-enabled="${item.enabled ? "1" : "0"}" ${busy || !authLoaded ? "disabled" : ""}>${enableLabel}</button>
|
|
11422
|
+
</div>
|
|
11423
|
+
</div></div></div>
|
|
11388
11424
|
</div>`;
|
|
11389
11425
|
}).join("");
|
|
11390
11426
|
return `<section class="sv-provider-section" data-stop="1">
|
|
11391
11427
|
<div class="sv-open-title">Connections</div>
|
|
11392
|
-
<div class="sv-open-intro">Relay uses each app's local profile. Your credentials stay with the app.</div>
|
|
11393
11428
|
<div class="sv-provider-list sv-agent-provider-list">${rows}</div>
|
|
11394
11429
|
<div class="sv-hint${providerInventoryRefreshing || providerInventoryStale || providerInventoryError ? "" : " hidden"}" id="svProviderInventoryState">${esc(providerInventoryStateText())}</div>
|
|
11395
11430
|
${providerAuthNote ? `<div class="sv-note">${esc(providerAuthNote)}</div>` : ""}
|
|
@@ -11810,6 +11845,19 @@
|
|
|
11810
11845
|
for (const button of settingsViewEl.querySelectorAll("[data-provider-connect]")) {
|
|
11811
11846
|
button.addEventListener("click", () => connectProviderFromSettings(button.getAttribute("data-provider-connect")));
|
|
11812
11847
|
}
|
|
11848
|
+
for (const button of settingsViewEl.querySelectorAll("[data-provider-details]")) {
|
|
11849
|
+
button.addEventListener("click", () => {
|
|
11850
|
+
const provider = button.getAttribute("data-provider-details");
|
|
11851
|
+
const willOpen = button.getAttribute("aria-expanded") !== "true";
|
|
11852
|
+
providerDetailsOpen = willOpen ? provider : "";
|
|
11853
|
+
for (const other of settingsViewEl.querySelectorAll("[data-provider-details]")) {
|
|
11854
|
+
const open = willOpen && other === button;
|
|
11855
|
+
other.setAttribute("aria-expanded", open ? "true" : "false");
|
|
11856
|
+
const detail = document.getElementById(other.getAttribute("aria-controls"));
|
|
11857
|
+
if (detail) detail.classList.toggle("open", open);
|
|
11858
|
+
}
|
|
11859
|
+
});
|
|
11860
|
+
}
|
|
11813
11861
|
for (const button of settingsViewEl.querySelectorAll("[data-provider-enable]")) {
|
|
11814
11862
|
button.addEventListener("click", () => setProviderEnabledFromSettings(
|
|
11815
11863
|
button.getAttribute("data-provider-enable"),
|
package/overlay/main.cjs
CHANGED
|
@@ -5161,8 +5161,12 @@ async function sendPreviewReply(input, entry) {
|
|
|
5161
5161
|
}
|
|
5162
5162
|
}
|
|
5163
5163
|
|
|
5164
|
+
function isTaggedAgentWorkRow(row) {
|
|
5165
|
+
return Boolean(row?.source?.host === "relay-agent-run" && row?.source?.agentSessionId);
|
|
5166
|
+
}
|
|
5167
|
+
|
|
5164
5168
|
function agentWorkEnabledForRow(row) {
|
|
5165
|
-
if (row
|
|
5169
|
+
if (isTaggedAgentWorkRow(row)) return PRODUCT_FEATURES.requests === true;
|
|
5166
5170
|
if (row?.relayNotificationKind === "task") return PRODUCT_FEATURES.requests === true;
|
|
5167
5171
|
if (row?.relayNotificationKind === "plain_relay") return PRODUCT_FEATURES.relayWork === true;
|
|
5168
5172
|
return false;
|
|
@@ -6149,7 +6153,8 @@ async function previewTaskSession(relayId) {
|
|
|
6149
6153
|
const id = String(relayId || "");
|
|
6150
6154
|
const row = rowById(id);
|
|
6151
6155
|
const isRequest = row?.relayNotificationKind === "task";
|
|
6152
|
-
const isLocalWork = row?.relayNotificationKind === "plain_relay" && Boolean(row?.workStartedAt)
|
|
6156
|
+
const isLocalWork = (row?.relayNotificationKind === "plain_relay" && Boolean(row?.workStartedAt))
|
|
6157
|
+
|| isTaggedAgentWorkRow(row);
|
|
6153
6158
|
if (!row || (!isRequest && !isLocalWork)) return { ok: false, error: "No local agent work exists for this Relay." };
|
|
6154
6159
|
if (!agentWorkEnabledForRow(row)) return agentWorkUnavailable();
|
|
6155
6160
|
const claudePath = row.claudeNativeSession && row.claudeNativeSession.sessionPath;
|
|
@@ -6497,7 +6502,13 @@ async function previewTaskSteer(input) {
|
|
|
6497
6502
|
if (body.length > 20000) return { ok: false, error: "That is too long to send." };
|
|
6498
6503
|
const row = rowById(id);
|
|
6499
6504
|
const isRequest = row?.relayNotificationKind === "task";
|
|
6500
|
-
|
|
6505
|
+
// @Claude/@Codex Work rows are server-authored plain chat responses. Their
|
|
6506
|
+
// durable session identity lives in source.agentSessionId, not in the
|
|
6507
|
+
// legacy workStartedAt field used by recipient-started ordinary Relay Work.
|
|
6508
|
+
// Treating only the legacy shape as Work lets the transcript render but
|
|
6509
|
+
// rejects every same-session follow-up before it reaches the session API.
|
|
6510
|
+
const isLocalWork = (row?.relayNotificationKind === "plain_relay" && Boolean(row?.workStartedAt))
|
|
6511
|
+
|| isTaggedAgentWorkRow(row);
|
|
6501
6512
|
if (!row || (!isRequest && !isLocalWork)) return { ok: false, error: "No local agent work exists for this Relay." };
|
|
6502
6513
|
if (!agentWorkEnabledForRow(row)) return agentWorkUnavailable();
|
|
6503
6514
|
if (row.source?.host === "relay-agent-run") {
|
package/package.json
CHANGED
package/src/task-daemon.js
CHANGED
|
@@ -92,6 +92,9 @@ function freshPlainRelays(ledger, items) {
|
|
|
92
92
|
!seen ||
|
|
93
93
|
seen.updatedAt !== item.updatedAt ||
|
|
94
94
|
seen.state !== item.state ||
|
|
95
|
+
(seen.taskStartedAt || "") !== (item.taskStartedAt || "") ||
|
|
96
|
+
(seen.taskCompletedAt || "") !== (item.taskCompletedAt || "") ||
|
|
97
|
+
(seen.taskRunOwner || "null") !== JSON.stringify(item.taskRunOwner || null) ||
|
|
95
98
|
(seen.restoredAt || "") !== (item.restoredAt || "")
|
|
96
99
|
);
|
|
97
100
|
});
|
|
@@ -104,6 +107,14 @@ function markPlainRelaysProcessed(ledger, items) {
|
|
|
104
107
|
ledger.plainRelays[item.relayId] = {
|
|
105
108
|
state: item.state,
|
|
106
109
|
updatedAt: item.updatedAt || item.createdAt || "",
|
|
110
|
+
// Task lifecycle receipts are projected independently of the Relay row.
|
|
111
|
+
// Starting or completing a Task deliberately does not edit the original
|
|
112
|
+
// Relay's updatedAt, so these fields must participate in the daemon's
|
|
113
|
+
// generation key or the pill keeps rendering its initially staged
|
|
114
|
+
// "Waiting on you" copy forever.
|
|
115
|
+
taskStartedAt: item.taskStartedAt || "",
|
|
116
|
+
taskCompletedAt: item.taskCompletedAt || "",
|
|
117
|
+
taskRunOwner: JSON.stringify(item.taskRunOwner || null),
|
|
107
118
|
restoredAt: item.restoredAt || "",
|
|
108
119
|
processedAt,
|
|
109
120
|
};
|