tandem-editor 0.12.0 → 0.13.5
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +121 -9
- package/README.md +142 -237
- package/dist/channel/index.js +158 -69
- package/dist/channel/index.js.map +1 -1
- package/dist/cli/index.js +656 -148
- package/dist/cli/index.js.map +1 -1
- package/dist/client/assets/CoworkSettings-BEvEpByR.js +3 -0
- package/dist/client/assets/CoworkSettings-C-1BnhBH.css +1 -0
- package/dist/client/assets/index-CXkPU0mo.css +1 -0
- package/dist/client/assets/index-D6kdw28e.js +238 -0
- package/dist/client/assets/index-Dm_QtxGQ.js +1 -0
- package/dist/client/assets/webview-KiZyy_pC.js +1 -0
- package/dist/client/fonts/OFL-Hanuman.txt +93 -0
- package/dist/client/fonts/OFL-InterTight.txt +93 -0
- package/dist/client/fonts/OFL-JetBrainsMono.txt +93 -0
- package/dist/client/fonts/OFL-SNPro.txt +93 -0
- package/dist/client/fonts/OFL-Sono.txt +93 -0
- package/dist/client/fonts/OFL-SourceSerif4.txt +93 -0
- package/dist/client/fonts/hanuman-latin.woff2 +0 -0
- package/dist/client/fonts/jetbrains-mono-latin.woff2 +0 -0
- package/dist/client/fonts/sn-pro-latin.woff2 +0 -0
- package/dist/client/fonts/sono-latin.woff2 +0 -0
- package/dist/client/fonts/source-serif-4-latin.woff2 +0 -0
- package/dist/client/index.html +341 -17
- package/dist/client/logo.png +0 -0
- package/dist/monitor/index.js +235 -161
- package/dist/monitor/index.js.map +1 -1
- package/dist/server/index.js +25323 -21754
- package/dist/server/index.js.map +1 -1
- package/package.json +11 -3
- package/sample/welcome.md +6 -6
- package/skills/tandem/SKILL.md +15 -0
- package/dist/client/assets/CoworkSettings-C0cS9R7L.js +0 -3
- package/dist/client/assets/index-Dn5JwXA3.css +0 -1
- package/dist/client/assets/index-n-vFW5By.js +0 -299
- package/dist/client/assets/webview-Bhf-n_os.js +0 -1
package/dist/channel/index.js
CHANGED
|
@@ -18007,6 +18007,7 @@ var TANDEM_REPO_URL = "https://github.com/bloknayrb/tandem";
|
|
|
18007
18007
|
var TANDEM_ISSUES_NEW_URL = `${TANDEM_REPO_URL}/issues/new`;
|
|
18008
18008
|
var MAX_FILE_SIZE = 50 * 1024 * 1024;
|
|
18009
18009
|
var SESSION_MAX_AGE = 30 * 24 * 60 * 60 * 1e3;
|
|
18010
|
+
var TANDEM_MODE_DEFAULT = "tandem";
|
|
18010
18011
|
var CHANNEL_MAX_RETRIES = 5;
|
|
18011
18012
|
var CHANNEL_RETRY_DELAY_MS = 2e3;
|
|
18012
18013
|
var CHANNEL_CONNECT_FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -18219,52 +18220,82 @@ var ToolErrorCodeSchema = external_exports.enum([
|
|
|
18219
18220
|
var ChannelErrorCodeSchema = external_exports.enum(["CHANNEL_CONNECT_FAILED", "MONITOR_CONNECT_FAILED"]);
|
|
18220
18221
|
var CHANNEL_CONNECT_FAILED = "CHANNEL_CONNECT_FAILED";
|
|
18221
18222
|
|
|
18222
|
-
// src/
|
|
18223
|
+
// src/shared/sse-consumer.ts
|
|
18223
18224
|
var AWARENESS_DEBOUNCE_MS = 500;
|
|
18225
|
+
var AWARENESS_CLEAR_MS = 3e3;
|
|
18224
18226
|
var MODE_CACHE_TTL_MS = 2e3;
|
|
18225
|
-
|
|
18227
|
+
var STABLE_CONNECTION_MS = 6e4;
|
|
18228
|
+
var RETRY_MAX_DELAY_MS = 3e4;
|
|
18229
|
+
var shutdownTimers = { awarenessTimer: null, clearAwarenessTimer: null, lastDocumentId: null };
|
|
18230
|
+
var outstandingAwareness = /* @__PURE__ */ new Set();
|
|
18231
|
+
function trackAwareness(p) {
|
|
18232
|
+
outstandingAwareness.add(p);
|
|
18233
|
+
p.finally(() => outstandingAwareness.delete(p));
|
|
18234
|
+
}
|
|
18235
|
+
var cachedMode = TANDEM_MODE_DEFAULT;
|
|
18236
|
+
var cachedModeAt = 0;
|
|
18237
|
+
var cachedModeFailedAt = 0;
|
|
18238
|
+
var _modeRefreshInFlight = null;
|
|
18239
|
+
async function runEventConsumer(opts) {
|
|
18240
|
+
await getCachedMode(opts.tandemUrl, opts.logPrefix).catch(() => {
|
|
18241
|
+
});
|
|
18226
18242
|
let retries = 0;
|
|
18227
18243
|
let lastEventId;
|
|
18228
18244
|
while (retries < CHANNEL_MAX_RETRIES) {
|
|
18229
18245
|
try {
|
|
18230
|
-
await
|
|
18231
|
-
|
|
18232
|
-
|
|
18246
|
+
await connectAndStreamOnce(opts, lastEventId, {
|
|
18247
|
+
onEventId: (id) => {
|
|
18248
|
+
lastEventId = id;
|
|
18249
|
+
},
|
|
18250
|
+
onStable: () => {
|
|
18251
|
+
retries = 0;
|
|
18252
|
+
}
|
|
18233
18253
|
});
|
|
18234
18254
|
} catch (err) {
|
|
18235
18255
|
retries++;
|
|
18236
18256
|
console.error(
|
|
18237
|
-
|
|
18257
|
+
`${opts.logPrefix} SSE connection failed (${retries}/${CHANNEL_MAX_RETRIES}):`,
|
|
18238
18258
|
err instanceof Error ? err.message : err
|
|
18239
18259
|
);
|
|
18240
18260
|
if (retries >= CHANNEL_MAX_RETRIES) {
|
|
18241
|
-
console.error(
|
|
18261
|
+
console.error(`${opts.logPrefix} SSE connection exhausted, reporting error and exiting`);
|
|
18242
18262
|
try {
|
|
18243
18263
|
await fetchWithTimeout(
|
|
18244
|
-
`${tandemUrl}${API_CHANNEL_ERROR}`,
|
|
18264
|
+
`${opts.tandemUrl}${API_CHANNEL_ERROR}`,
|
|
18245
18265
|
{
|
|
18246
18266
|
method: "POST",
|
|
18247
18267
|
headers: { "Content-Type": "application/json" },
|
|
18248
18268
|
body: JSON.stringify({
|
|
18249
|
-
error:
|
|
18250
|
-
message:
|
|
18269
|
+
error: opts.errorCode,
|
|
18270
|
+
message: `${opts.logPrefix} lost connection after ${CHANNEL_MAX_RETRIES} retries.`
|
|
18251
18271
|
})
|
|
18252
18272
|
},
|
|
18253
18273
|
CHANNEL_ERROR_REPORT_TIMEOUT_MS
|
|
18254
18274
|
);
|
|
18255
18275
|
} catch (reportErr) {
|
|
18256
18276
|
console.error(
|
|
18257
|
-
|
|
18277
|
+
`${opts.logPrefix} Could not report failure to server:`,
|
|
18258
18278
|
describeFetchError(reportErr, API_CHANNEL_ERROR, CHANNEL_ERROR_REPORT_TIMEOUT_MS)
|
|
18259
18279
|
);
|
|
18260
18280
|
}
|
|
18281
|
+
opts.onExhaustion?.();
|
|
18261
18282
|
process.exit(1);
|
|
18262
18283
|
}
|
|
18263
|
-
|
|
18284
|
+
const delay = Math.min(CHANNEL_RETRY_DELAY_MS * 2 ** (retries - 1), RETRY_MAX_DELAY_MS);
|
|
18285
|
+
console.error(
|
|
18286
|
+
`${opts.logPrefix} Retrying in ${delay}ms (attempt ${retries}/${CHANNEL_MAX_RETRIES})...`
|
|
18287
|
+
);
|
|
18288
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
18264
18289
|
}
|
|
18265
18290
|
}
|
|
18291
|
+
console.error(
|
|
18292
|
+
`${opts.logPrefix} Retry loop exited unexpectedly (retries=${retries}/${CHANNEL_MAX_RETRIES})`
|
|
18293
|
+
);
|
|
18294
|
+
process.exit(1);
|
|
18266
18295
|
}
|
|
18267
|
-
async function
|
|
18296
|
+
async function connectAndStreamOnce(opts, lastEventId, cb) {
|
|
18297
|
+
const onStable = cb.onStable ?? (() => {
|
|
18298
|
+
});
|
|
18268
18299
|
const headers = { Accept: "text/event-stream" };
|
|
18269
18300
|
if (lastEventId) headers["Last-Event-ID"] = lastEventId;
|
|
18270
18301
|
const connectCtrl = new AbortController();
|
|
@@ -18274,12 +18305,16 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
|
|
|
18274
18305
|
);
|
|
18275
18306
|
let res;
|
|
18276
18307
|
try {
|
|
18277
|
-
res = await authFetch(`${tandemUrl}${API_EVENTS}`, {
|
|
18308
|
+
res = await authFetch(`${opts.tandemUrl}${API_EVENTS}`, {
|
|
18309
|
+
headers,
|
|
18310
|
+
signal: connectCtrl.signal
|
|
18311
|
+
});
|
|
18278
18312
|
} finally {
|
|
18279
18313
|
clearTimeout(connectTimer);
|
|
18280
18314
|
}
|
|
18281
18315
|
if (!res.ok) throw new Error(`SSE endpoint returned ${res.status}`);
|
|
18282
18316
|
if (!res.body) throw new Error("SSE endpoint returned no body");
|
|
18317
|
+
const stableTimer = setTimeout(onStable, STABLE_CONNECTION_MS);
|
|
18283
18318
|
const reader = res.body.getReader();
|
|
18284
18319
|
const decoder = new TextDecoder();
|
|
18285
18320
|
let buffer = "";
|
|
@@ -18292,13 +18327,10 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
|
|
|
18292
18327
|
});
|
|
18293
18328
|
}
|
|
18294
18329
|
}, CHANNEL_SSE_INACTIVITY_TIMEOUT_MS / 4);
|
|
18295
|
-
let awarenessTimer = null;
|
|
18296
|
-
let clearAwarenessTimer = null;
|
|
18297
18330
|
let pendingAwareness = null;
|
|
18298
|
-
|
|
18299
|
-
|
|
18300
|
-
|
|
18301
|
-
`${tandemUrl}${API_CHANNEL_AWARENESS}`,
|
|
18331
|
+
function clearAwarenessNow(documentId) {
|
|
18332
|
+
const p = fetchWithTimeout(
|
|
18333
|
+
`${opts.tandemUrl}${API_CHANNEL_AWARENESS}`,
|
|
18302
18334
|
{
|
|
18303
18335
|
method: "POST",
|
|
18304
18336
|
headers: { "Content-Type": "application/json" },
|
|
@@ -18311,7 +18343,7 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
|
|
|
18311
18343
|
CHANNEL_AWARENESS_FETCH_TIMEOUT_MS
|
|
18312
18344
|
).catch((err) => {
|
|
18313
18345
|
console.error(
|
|
18314
|
-
|
|
18346
|
+
`${opts.logPrefix} Awareness clear failed:`,
|
|
18315
18347
|
describeFetchError(
|
|
18316
18348
|
err,
|
|
18317
18349
|
`${API_CHANNEL_AWARENESS} clear`,
|
|
@@ -18319,13 +18351,15 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
|
|
|
18319
18351
|
)
|
|
18320
18352
|
);
|
|
18321
18353
|
});
|
|
18354
|
+
trackAwareness(p);
|
|
18322
18355
|
}
|
|
18323
18356
|
function flushAwareness() {
|
|
18324
18357
|
if (!pendingAwareness) return;
|
|
18325
18358
|
const event = pendingAwareness;
|
|
18326
18359
|
pendingAwareness = null;
|
|
18327
|
-
|
|
18328
|
-
|
|
18360
|
+
if (event.documentId) shutdownTimers.lastDocumentId = event.documentId;
|
|
18361
|
+
const p = fetchWithTimeout(
|
|
18362
|
+
`${opts.tandemUrl}${API_CHANNEL_AWARENESS}`,
|
|
18329
18363
|
{
|
|
18330
18364
|
method: "POST",
|
|
18331
18365
|
headers: { "Content-Type": "application/json" },
|
|
@@ -18338,7 +18372,7 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
|
|
|
18338
18372
|
CHANNEL_AWARENESS_FETCH_TIMEOUT_MS
|
|
18339
18373
|
).catch((err) => {
|
|
18340
18374
|
console.error(
|
|
18341
|
-
|
|
18375
|
+
`${opts.logPrefix} Awareness update failed:`,
|
|
18342
18376
|
describeFetchError(
|
|
18343
18377
|
err,
|
|
18344
18378
|
`${API_CHANNEL_AWARENESS} update`,
|
|
@@ -18346,13 +18380,17 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
|
|
|
18346
18380
|
)
|
|
18347
18381
|
);
|
|
18348
18382
|
});
|
|
18349
|
-
|
|
18350
|
-
|
|
18383
|
+
trackAwareness(p);
|
|
18384
|
+
if (shutdownTimers.clearAwarenessTimer) clearTimeout(shutdownTimers.clearAwarenessTimer);
|
|
18385
|
+
shutdownTimers.clearAwarenessTimer = setTimeout(
|
|
18386
|
+
() => clearAwarenessNow(event.documentId),
|
|
18387
|
+
AWARENESS_CLEAR_MS
|
|
18388
|
+
);
|
|
18351
18389
|
}
|
|
18352
18390
|
function scheduleAwareness(event) {
|
|
18353
18391
|
pendingAwareness = event;
|
|
18354
|
-
if (awarenessTimer) clearTimeout(awarenessTimer);
|
|
18355
|
-
awarenessTimer = setTimeout(flushAwareness, AWARENESS_DEBOUNCE_MS);
|
|
18392
|
+
if (shutdownTimers.awarenessTimer) clearTimeout(shutdownTimers.awarenessTimer);
|
|
18393
|
+
shutdownTimers.awarenessTimer = setTimeout(flushAwareness, AWARENESS_DEBOUNCE_MS);
|
|
18356
18394
|
}
|
|
18357
18395
|
try {
|
|
18358
18396
|
while (true) {
|
|
@@ -18380,84 +18418,135 @@ async function connectAndStream(mcp, tandemUrl, lastEventId, onEventId) {
|
|
|
18380
18418
|
else if (line.startsWith("data: ")) data = line.slice(6);
|
|
18381
18419
|
}
|
|
18382
18420
|
if (!data) continue;
|
|
18383
|
-
let
|
|
18421
|
+
let raw;
|
|
18384
18422
|
try {
|
|
18385
|
-
|
|
18386
|
-
} catch {
|
|
18423
|
+
raw = JSON.parse(data);
|
|
18424
|
+
} catch (err) {
|
|
18387
18425
|
console.error(
|
|
18388
|
-
|
|
18389
|
-
|
|
18390
|
-
data.slice(0, 200)
|
|
18426
|
+
`${opts.logPrefix} SSE JSON parse failed (eventId=${eventId ?? "none"}, len=${data.length}): ${err instanceof Error ? err.message : err}. Tail:`,
|
|
18427
|
+
data.slice(Math.max(0, data.length - 200))
|
|
18391
18428
|
);
|
|
18392
|
-
if (eventId) onEventId(eventId);
|
|
18429
|
+
if (eventId) cb.onEventId(eventId);
|
|
18393
18430
|
continue;
|
|
18394
18431
|
}
|
|
18432
|
+
const event = parseTandemEvent(raw);
|
|
18395
18433
|
if (!event) {
|
|
18396
18434
|
console.error(
|
|
18397
|
-
|
|
18398
|
-
eventId,
|
|
18399
|
-
data.slice(0, 200)
|
|
18435
|
+
`${opts.logPrefix} SSE event failed validation (eventId=${eventId ?? "none"}): shape mismatch`
|
|
18400
18436
|
);
|
|
18401
|
-
if (eventId) onEventId(eventId);
|
|
18437
|
+
if (eventId) cb.onEventId(eventId);
|
|
18402
18438
|
continue;
|
|
18403
18439
|
}
|
|
18404
18440
|
if (event.type !== "chat:message") {
|
|
18405
|
-
|
|
18406
|
-
if (
|
|
18407
|
-
console.error(
|
|
18408
|
-
if (eventId) onEventId(eventId);
|
|
18441
|
+
refreshMode(opts.tandemUrl, opts.logPrefix);
|
|
18442
|
+
if (getModeSync() === "solo") {
|
|
18443
|
+
console.error(`${opts.logPrefix} Solo mode: suppressed ${event.type} event`);
|
|
18444
|
+
if (eventId) cb.onEventId(eventId);
|
|
18409
18445
|
continue;
|
|
18410
18446
|
}
|
|
18411
18447
|
}
|
|
18412
18448
|
try {
|
|
18413
|
-
await
|
|
18414
|
-
method: "notifications/claude/channel",
|
|
18415
|
-
params: {
|
|
18416
|
-
content: formatEventContent(event),
|
|
18417
|
-
meta: formatEventMeta(event)
|
|
18418
|
-
}
|
|
18419
|
-
});
|
|
18449
|
+
await opts.onEvent(event, eventId);
|
|
18420
18450
|
} catch (err) {
|
|
18421
|
-
console.error(
|
|
18451
|
+
console.error(`${opts.logPrefix} onEvent failed (transport broken?):`, err);
|
|
18422
18452
|
throw err;
|
|
18423
18453
|
}
|
|
18424
|
-
if (eventId) onEventId(eventId);
|
|
18454
|
+
if (eventId) cb.onEventId(eventId);
|
|
18425
18455
|
scheduleAwareness(event);
|
|
18426
18456
|
}
|
|
18427
18457
|
}
|
|
18428
18458
|
} finally {
|
|
18459
|
+
clearTimeout(stableTimer);
|
|
18429
18460
|
clearInterval(watchdog);
|
|
18430
|
-
if (awarenessTimer) clearTimeout(awarenessTimer);
|
|
18431
|
-
if (clearAwarenessTimer) clearTimeout(clearAwarenessTimer);
|
|
18461
|
+
if (shutdownTimers.awarenessTimer) clearTimeout(shutdownTimers.awarenessTimer);
|
|
18462
|
+
if (shutdownTimers.clearAwarenessTimer) clearTimeout(shutdownTimers.clearAwarenessTimer);
|
|
18463
|
+
shutdownTimers.awarenessTimer = null;
|
|
18464
|
+
shutdownTimers.clearAwarenessTimer = null;
|
|
18465
|
+
pendingAwareness = null;
|
|
18432
18466
|
}
|
|
18433
18467
|
}
|
|
18434
|
-
|
|
18435
|
-
var cachedModeAt = 0;
|
|
18436
|
-
async function getCachedMode(tandemUrl) {
|
|
18437
|
-
const now = Date.now();
|
|
18438
|
-
if (now - cachedModeAt < MODE_CACHE_TTL_MS) return cachedMode;
|
|
18468
|
+
async function fetchMode(tandemUrl) {
|
|
18439
18469
|
try {
|
|
18440
18470
|
const res = await fetchWithTimeout(
|
|
18441
18471
|
`${tandemUrl}${API_MODE}`,
|
|
18442
18472
|
{},
|
|
18443
18473
|
CHANNEL_MODE_FETCH_TIMEOUT_MS
|
|
18444
18474
|
);
|
|
18445
|
-
if (res.ok) {
|
|
18446
|
-
|
|
18447
|
-
|
|
18448
|
-
|
|
18449
|
-
|
|
18450
|
-
}
|
|
18451
|
-
cachedModeAt = now;
|
|
18475
|
+
if (!res.ok) return { ok: false, reason: `status ${res.status}` };
|
|
18476
|
+
const body = await res.json();
|
|
18477
|
+
const parsed = TandemModeSchema.safeParse(body.mode);
|
|
18478
|
+
if (!parsed.success) return { ok: false, reason: `invalid mode ${JSON.stringify(body.mode)}` };
|
|
18479
|
+
return { ok: true, mode: parsed.data };
|
|
18452
18480
|
} catch (err) {
|
|
18481
|
+
return { ok: false, reason: describeFetchError(err, API_MODE, CHANNEL_MODE_FETCH_TIMEOUT_MS) };
|
|
18482
|
+
}
|
|
18483
|
+
}
|
|
18484
|
+
async function getCachedMode(tandemUrl, logPrefix = "[Tandem]") {
|
|
18485
|
+
const now = Date.now();
|
|
18486
|
+
if (now - cachedModeAt < MODE_CACHE_TTL_MS && cachedModeAt !== 0) return cachedMode;
|
|
18487
|
+
const result = await fetchMode(tandemUrl);
|
|
18488
|
+
if (!result.ok) {
|
|
18489
|
+
if (cachedModeAt !== 0) {
|
|
18490
|
+
console.error(
|
|
18491
|
+
`${logPrefix} Mode check failed (${result.reason}), preserving last known mode '${cachedMode}'`
|
|
18492
|
+
);
|
|
18493
|
+
return cachedMode;
|
|
18494
|
+
}
|
|
18453
18495
|
console.error(
|
|
18454
|
-
|
|
18455
|
-
describeFetchError(err, API_MODE, CHANNEL_MODE_FETCH_TIMEOUT_MS)
|
|
18496
|
+
`${logPrefix} Mode check failed (${result.reason}), no prior mode \u2014 using cold-start default '${TANDEM_MODE_DEFAULT}'`
|
|
18456
18497
|
);
|
|
18457
|
-
|
|
18498
|
+
cachedMode = TANDEM_MODE_DEFAULT;
|
|
18499
|
+
return TANDEM_MODE_DEFAULT;
|
|
18458
18500
|
}
|
|
18501
|
+
cachedMode = result.mode;
|
|
18502
|
+
cachedModeAt = now;
|
|
18503
|
+
return cachedMode;
|
|
18504
|
+
}
|
|
18505
|
+
function getModeSync() {
|
|
18459
18506
|
return cachedMode;
|
|
18460
18507
|
}
|
|
18508
|
+
function refreshMode(tandemUrl, logPrefix) {
|
|
18509
|
+
if (_modeRefreshInFlight) return;
|
|
18510
|
+
const now = Date.now();
|
|
18511
|
+
if (now - cachedModeAt < MODE_CACHE_TTL_MS) return;
|
|
18512
|
+
if (now - cachedModeFailedAt < MODE_CACHE_TTL_MS) return;
|
|
18513
|
+
_modeRefreshInFlight = (async () => {
|
|
18514
|
+
try {
|
|
18515
|
+
const result = await fetchMode(tandemUrl);
|
|
18516
|
+
if (result.ok) {
|
|
18517
|
+
cachedMode = result.mode;
|
|
18518
|
+
cachedModeAt = Date.now();
|
|
18519
|
+
cachedModeFailedAt = 0;
|
|
18520
|
+
} else {
|
|
18521
|
+
cachedModeFailedAt = Date.now();
|
|
18522
|
+
console.error(
|
|
18523
|
+
`${logPrefix} Background mode refresh failed (${result.reason}), keeping cached`
|
|
18524
|
+
);
|
|
18525
|
+
}
|
|
18526
|
+
} finally {
|
|
18527
|
+
_modeRefreshInFlight = null;
|
|
18528
|
+
}
|
|
18529
|
+
})().catch((err) => {
|
|
18530
|
+
console.error(`${logPrefix} refreshMode unexpected error:`, err);
|
|
18531
|
+
cachedModeFailedAt = Date.now();
|
|
18532
|
+
});
|
|
18533
|
+
}
|
|
18534
|
+
|
|
18535
|
+
// src/channel/event-bridge.ts
|
|
18536
|
+
async function startEventBridge(mcp, tandemUrl) {
|
|
18537
|
+
return runEventConsumer({
|
|
18538
|
+
tandemUrl,
|
|
18539
|
+
logPrefix: "[Channel]",
|
|
18540
|
+
errorCode: CHANNEL_CONNECT_FAILED,
|
|
18541
|
+
onEvent: (event) => mcp.notification({
|
|
18542
|
+
method: "notifications/claude/channel",
|
|
18543
|
+
params: {
|
|
18544
|
+
content: formatEventContent(event),
|
|
18545
|
+
meta: formatEventMeta(event)
|
|
18546
|
+
}
|
|
18547
|
+
})
|
|
18548
|
+
});
|
|
18549
|
+
}
|
|
18461
18550
|
|
|
18462
18551
|
// src/channel/run.ts
|
|
18463
18552
|
async function runChannel(opts = {}) {
|