conductor-remote 1.69.0 → 1.70.0
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/dist/assets/index-CMOaVjNQ.js +47 -0
- package/dist/index.html +1 -1
- package/dist/sw.js +1 -1
- package/dist-node/src/mcp-tools.js +14 -3
- package/dist-node/src/server.js +19 -5
- package/dist-node/src/transcript.js +19 -0
- package/package.json +1 -1
- package/dist/assets/index-2nsPbh1M.js +0 -47
package/dist/index.html
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<title>Conductor Remote</title>
|
|
14
14
|
<!-- Runs before the module bundle so it can catch a stale shell that fails to boot. -->
|
|
15
15
|
<script src="/self-heal.js"></script>
|
|
16
|
-
<script type="module" crossorigin src="/assets/index-
|
|
16
|
+
<script type="module" crossorigin src="/assets/index-CMOaVjNQ.js"></script>
|
|
17
17
|
<link rel="stylesheet" crossorigin href="/assets/index-CS-jxAPg.css">
|
|
18
18
|
<link rel="manifest" href="/manifest.webmanifest"></head>
|
|
19
19
|
<body>
|
package/dist/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const t=e=>i(e,o),c={module:{uri:o},exports:l,require:t};s[o]=Promise.all(n.map(e=>c[e]||t(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"
|
|
1
|
+
if(!self.define){let e,s={};const i=(i,n)=>(i=new URL(i+".js",n).href,s[i]||new Promise(s=>{if("document"in self){const e=document.createElement("script");e.src=i,e.onload=s,document.head.appendChild(e)}else e=i,importScripts(i),s()}).then(()=>{let e=s[i];if(!e)throw new Error(`Module ${i} didn’t register its module`);return e}));self.define=(n,r)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(s[o])return;let l={};const t=e=>i(e,o),c={module:{uri:o},exports:l,require:t};s[o]=Promise.all(n.map(e=>c[e]||t(e))).then(e=>(r(...e),l))}}define(["./workbox-9c191d2f"],function(e){"use strict";importScripts("/push-sw.js"),self.addEventListener("message",e=>{e.data&&"SKIP_WAITING"===e.data.type&&self.skipWaiting()}),e.clientsClaim(),e.precacheAndRoute([{url:"self-heal.js",revision:"49bd63adb25a09341f8d2610e8bd3c76"},{url:"push-sw.js",revision:"e1e682e2e5e88fa03b7808ae9db8e098"},{url:"index.html",revision:"9fd3d35917bdcc36eb68d82f29d8d01b"},{url:"assets/workbox-window.prod.es5-BBnX5xw4.js",revision:null},{url:"assets/index-CS-jxAPg.css",revision:null},{url:"assets/index-CMOaVjNQ.js",revision:null},{url:"apple-touch-icon.png",revision:"2b9301416b880d45d4bb655f2600d1f2"},{url:"icon-192.png",revision:"c5e01ac58768627e18ee7b8b6a9239ef"},{url:"icon-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon-maskable-512.png",revision:"a40638c55e310312457a621c9a0002c8"},{url:"icon.svg",revision:"c1aee186821798733dd477e69a0ef243"},{url:"manifest.webmanifest",revision:"cf88fbc5755108a7fe0616fa160a8a15"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("/index.html"),{denylist:[/^\/api\//]}))});
|
|
@@ -436,7 +436,7 @@ export function createTools(call) {
|
|
|
436
436
|
},
|
|
437
437
|
{
|
|
438
438
|
name: 'split_chat',
|
|
439
|
-
description: 'Move a tangent out of a chat: copy that chat into a fresh tab beside it, as a Conductor attachment, and ask the new agent your question there. Use it when a conversation has grown a second topic — a running agent steered mid-turn ends up holding three threads at once, which reads badly for everyone afterwards. The copy carries the prose and the reasoning, not the tool calls, so the new agent knows what was said and decided but not every file that was read. This DRIVES THE REAL UI twice (a new tab, then the send) and steals focus for a few seconds. To split the chat you are in, find its session_id with list_chats on your own workspace.',
|
|
439
|
+
description: 'Move a tangent out of a chat: copy that chat into a fresh tab beside it, as a Conductor attachment, and ask the new agent your question there. Use it when a conversation has grown a second topic — a running agent steered mid-turn ends up holding three threads at once, which reads badly for everyone afterwards. The copy carries the prose and the reasoning, not the tool calls, so the new agent knows what was said and decided but not every file that was read. It runs to the end of the chat unless through names an earlier message to stop at, which is how you branch from before the conversation turned. This DRIVES THE REAL UI twice (a new tab, then the send) and steals focus for a few seconds. To split the chat you are in, find its session_id with list_chats on your own workspace.',
|
|
440
440
|
inputSchema: {
|
|
441
441
|
type: 'object',
|
|
442
442
|
properties: {
|
|
@@ -450,6 +450,10 @@ export function createTools(call) {
|
|
|
450
450
|
include_tools: {
|
|
451
451
|
type: 'boolean',
|
|
452
452
|
description: 'Carry tool calls across as one line each (default false — mostly noise, and most of the bytes).'
|
|
453
|
+
},
|
|
454
|
+
through: {
|
|
455
|
+
type: 'string',
|
|
456
|
+
description: 'Stop the copy at this message, it included — a cursor from read_chat or search_chats. Default: the whole chat.'
|
|
453
457
|
}
|
|
454
458
|
},
|
|
455
459
|
required: ['session_id', 'prompt']
|
|
@@ -457,13 +461,19 @@ export function createTools(call) {
|
|
|
457
461
|
run: async (args) => {
|
|
458
462
|
const sessionId = need(args, 'session_id');
|
|
459
463
|
const prompt = need(args, 'prompt');
|
|
464
|
+
const through = str(args.through);
|
|
465
|
+
const throughRowid = through ? parseChatCursor(through) : null;
|
|
466
|
+
if (through && throughRowid === null) {
|
|
467
|
+
throw new Error('through must be a cursor returned by read_chat or search_chats');
|
|
468
|
+
}
|
|
460
469
|
const split = await call(routes.splitChat.path(sessionId), {
|
|
461
470
|
method: routes.splitChat.method,
|
|
462
471
|
body: {
|
|
463
472
|
prompt,
|
|
464
473
|
workspaceId: str(args.workspace_id),
|
|
465
474
|
includeThinking: args.include_thinking !== false,
|
|
466
|
-
includeTools: args.include_tools === true
|
|
475
|
+
includeTools: args.include_tools === true,
|
|
476
|
+
throughRowid: throughRowid ?? undefined
|
|
467
477
|
},
|
|
468
478
|
timeoutMs: WRITE_TIMEOUT_MS
|
|
469
479
|
});
|
|
@@ -474,7 +484,8 @@ export function createTools(call) {
|
|
|
474
484
|
// like a complete one to whoever gets it next.
|
|
475
485
|
const cut = [
|
|
476
486
|
file.elided.tools ? plural(file.elided.tools, 'tool call') : '',
|
|
477
|
-
file.elided.thinking ? plural(file.elided.thinking, 'thinking block') : ''
|
|
487
|
+
file.elided.thinking ? plural(file.elided.thinking, 'thinking block') : '',
|
|
488
|
+
file.elided.later ? plural(file.elided.later, 'entry after the cut', 'entries after the cut') : ''
|
|
478
489
|
].filter(Boolean);
|
|
479
490
|
const lines = [
|
|
480
491
|
`copied ${plural(file.kept, 'entry', 'entries')} (${Math.round(file.bytes / 1024)}kB) to ${file.path}${cut.length ? `, without ${cut.join(' or ')}` : ''}`
|
package/dist-node/src/server.js
CHANGED
|
@@ -30,7 +30,7 @@ import { readSettings, writeSettings } from "./settings.js";
|
|
|
30
30
|
import { withoutWindowEvidence } from "./shared.js";
|
|
31
31
|
import { discardStagedAttachment, materializeStagedAttachments, stageAttachment, stagedAttachments } from "./staged-attachments.js";
|
|
32
32
|
import { driftWarningLines, readExposeMode, tailscaleBin } from "./tailscale.js";
|
|
33
|
-
import { renderTranscript } from "./transcript.js";
|
|
33
|
+
import { renderTranscript, transcriptThrough } from "./transcript.js";
|
|
34
34
|
import { autoJoinHotspotMode, currentSsid, looksLikeHotspot, preferredNetworks } from "./wifi.js";
|
|
35
35
|
import { createWorkspace, describeActuator, EFFORT_LABELS, listAgentModels, lockBlocked, newChat, pickActuator, retryWontHelp, screenLocked, sendNeverStarted, setAgentOptions, setRestartGuard, setWorkspaceStatus, stopTurn, UiBusyError, uiQueueDepth, WORKSPACE_STATUS_LABELS, withUiPriority } from "./writes.js";
|
|
36
36
|
// Before anything that logs: from here on every console line is also kept in memory for
|
|
@@ -1494,7 +1494,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1494
1494
|
});
|
|
1495
1495
|
return json(req, res, answer.status, answer.body);
|
|
1496
1496
|
}
|
|
1497
|
-
// POST /api/sessions/:id/split { prompt?, includeThinking?, includeTools? }
|
|
1497
|
+
// POST /api/sessions/:id/split { prompt?, includeThinking?, includeTools?, throughRowid? }
|
|
1498
1498
|
//
|
|
1499
1499
|
// Conductor's own "Fork to new tab" resumes the agent's real session. This copies
|
|
1500
1500
|
// the conversation instead, as a Conductor attachment, which is the cut that
|
|
@@ -1527,7 +1527,15 @@ const server = http.createServer(async (req, res) => {
|
|
|
1527
1527
|
return json(req, res, 404, { error: 'chat not found in that workspace' });
|
|
1528
1528
|
const format = { thinking: body.includeThinking !== false, tools: body.includeTools === true };
|
|
1529
1529
|
const { entries } = reads.getMessages(sessionId);
|
|
1530
|
-
const
|
|
1530
|
+
const through = body.throughRowid;
|
|
1531
|
+
if (through !== undefined && (!Number.isSafeInteger(through) || through < 1)) {
|
|
1532
|
+
return json(req, res, 400, { error: 'throughRowid must be a positive integer' });
|
|
1533
|
+
}
|
|
1534
|
+
const cut = through === undefined ? { entries, later: 0 } : transcriptThrough(entries, through);
|
|
1535
|
+
if (!cut)
|
|
1536
|
+
return json(req, res, 409, { error: 'that message is not in this chat' });
|
|
1537
|
+
const rendered = renderTranscript(cut.entries, format);
|
|
1538
|
+
const elided = { ...rendered.elided, later: cut.later };
|
|
1531
1539
|
if (!rendered.kept)
|
|
1532
1540
|
return json(req, res, 409, { error: 'that chat has nothing to copy yet' });
|
|
1533
1541
|
// Conductor's own name for a copied transcript, so the chip reads the same as one
|
|
@@ -1536,18 +1544,24 @@ const server = http.createServer(async (req, res) => {
|
|
|
1536
1544
|
const title = source.title?.trim() || 'chat';
|
|
1537
1545
|
const carried = [`thinking ${format.thinking ? 'included' : 'omitted'}`];
|
|
1538
1546
|
carried.push(`tool calls ${format.tools ? 'included' : 'omitted'}`);
|
|
1547
|
+
const stops = cut.later
|
|
1548
|
+
? [
|
|
1549
|
+
`The copy stops partway through: ${cut.later} later ${cut.later === 1 ? 'entry is' : 'entries are'} not in it.`
|
|
1550
|
+
]
|
|
1551
|
+
: [];
|
|
1539
1552
|
const header = [
|
|
1540
1553
|
`# Transcript of ${title}`,
|
|
1541
1554
|
'',
|
|
1542
1555
|
`${[ws.repo_name, ws.branch].filter(Boolean).join(' · ')}`,
|
|
1543
1556
|
`Copied from the Conductor chat \`${sessionId}\` by conductor-remote. ${carried.join(', ')}.`,
|
|
1557
|
+
...stops,
|
|
1544
1558
|
'',
|
|
1545
1559
|
''
|
|
1546
1560
|
].join('\n');
|
|
1547
1561
|
const attachment = writeAttachment(ws.worktree, `Transcript of ${title}.md`, header + rendered.text);
|
|
1548
1562
|
const opened = await openChat(ws);
|
|
1549
1563
|
if ('error' in opened) {
|
|
1550
|
-
return json(req, res, 502, { ...opened.result, attachment: { ...attachment, ...rendered } });
|
|
1564
|
+
return json(req, res, 502, { ...opened.result, attachment: { ...attachment, ...rendered, elided } });
|
|
1551
1565
|
}
|
|
1552
1566
|
// The token is what Conductor turns into the attachment chip and supplies to the
|
|
1553
1567
|
// receiving agent. Do not repeat `attachment.relPath` in prose: that renders a
|
|
@@ -1563,7 +1577,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
1563
1577
|
path: attachment.relPath,
|
|
1564
1578
|
bytes: attachment.bytes,
|
|
1565
1579
|
kept: rendered.kept,
|
|
1566
|
-
elided
|
|
1580
|
+
elided
|
|
1567
1581
|
}
|
|
1568
1582
|
});
|
|
1569
1583
|
}
|
|
@@ -259,6 +259,25 @@ function toolLine(e) {
|
|
|
259
259
|
function plural(n, one) {
|
|
260
260
|
return `${n} ${one}${n === 1 ? '' : 's'}`;
|
|
261
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Cut a transcript at one message, that message included — a fork from an earlier point.
|
|
264
|
+
*
|
|
265
|
+
* The cut is made on the rowid rather than on a position, because that is the granularity
|
|
266
|
+
* every pointer into a chat already has: `read_chat`'s cursor is a rowid, and one source
|
|
267
|
+
* row yields several entries (the reasoning, the prose, the tool calls it made) that belong
|
|
268
|
+
* to the same message and have to cross together.
|
|
269
|
+
*
|
|
270
|
+
* Null means no message here carries that rowid — a cursor from a different chat, or one
|
|
271
|
+
* past the end of this one. Copying the whole chat in that case is the silent failure this
|
|
272
|
+
* exists to prevent: a transcript that stops at the wrong place reads exactly like one that
|
|
273
|
+
* stops where it was asked to.
|
|
274
|
+
*/
|
|
275
|
+
export function transcriptThrough(entries, rowid) {
|
|
276
|
+
if (!entries.some(entry => entry.rowid === rowid))
|
|
277
|
+
return null;
|
|
278
|
+
const kept = entries.filter(entry => entry.rowid <= rowid);
|
|
279
|
+
return { entries: kept, later: entries.length - kept.length };
|
|
280
|
+
}
|
|
262
281
|
/**
|
|
263
282
|
* A chat as markdown, in Conductor's own transcript layout.
|
|
264
283
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "conductor-remote",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.70.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"packageManager": "yarn@4.15.0",
|
|
6
6
|
"description": "Phone control panel for local Conductor agents. Reads ride SQLite + git; prompts ride Conductor's own dispatch path.",
|