runpane 2.4.186 → 2.4.187
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/README.md +31 -1
- package/dist/agentTasks.d.ts.map +1 -1
- package/dist/agentTasks.js +4 -0
- package/dist/agentTasks.js.map +1 -1
- package/dist/boundaryDecoder.d.ts.map +1 -1
- package/dist/boundaryDecoder.js +6 -1
- package/dist/boundaryDecoder.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +15 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +51 -1
- package/dist/commands.js.map +1 -1
- package/dist/docs-index.json +1 -1
- package/dist/generated/contract.d.ts +543 -36
- package/dist/generated/contract.d.ts.map +1 -1
- package/dist/generated/contract.js +903 -39
- package/dist/generated/contract.js.map +1 -1
- package/dist/localControl.d.ts +42 -2
- package/dist/localControl.d.ts.map +1 -1
- package/dist/localControl.js +276 -9
- package/dist/localControl.js.map +1 -1
- package/dist/watchLines.d.ts.map +1 -1
- package/dist/watchLines.js +3 -0
- package/dist/watchLines.js.map +1 -1
- package/package.json +1 -1
package/dist/localControl.js
CHANGED
|
@@ -37,6 +37,8 @@ exports.runPanelsInput = runPanelsInput;
|
|
|
37
37
|
exports.runPanelsScreen = runPanelsScreen;
|
|
38
38
|
exports.runPanelsLastMessage = runPanelsLastMessage;
|
|
39
39
|
exports.runReport = runReport;
|
|
40
|
+
exports.runInbox = runInbox;
|
|
41
|
+
exports.runInboxAck = runInboxAck;
|
|
40
42
|
exports.runPanelsSubmit = runPanelsSubmit;
|
|
41
43
|
exports.markSuggestionLine = markSuggestionLine;
|
|
42
44
|
exports.printPromptNotes = printPromptNotes;
|
|
@@ -54,6 +56,7 @@ const node_process_1 = require("node:process");
|
|
|
54
56
|
const promises_1 = require("node:readline/promises");
|
|
55
57
|
const boundaryDecoder_1 = require("./boundaryDecoder");
|
|
56
58
|
const daemonClient_1 = require("./daemonClient");
|
|
59
|
+
const errors_1 = require("./errors");
|
|
57
60
|
const contract_1 = require("./generated/contract");
|
|
58
61
|
const commands_1 = require("./commands");
|
|
59
62
|
const sessionPayload_1 = require("./sessionPayload");
|
|
@@ -182,6 +185,7 @@ const agentReportFields = {
|
|
|
182
185
|
reportedAt: boundaryDecoder_1.boundary.string,
|
|
183
186
|
};
|
|
184
187
|
const agentReportSchema = boundaryDecoder_1.boundary.object(agentReportFields);
|
|
188
|
+
const expectedReportSchema = boundaryDecoder_1.boundary.object({ since: boundaryDecoder_1.boundary.string });
|
|
185
189
|
const reportResultSchema = boundaryDecoder_1.boundary.object({
|
|
186
190
|
ok: boundaryDecoder_1.boundary.literal(true),
|
|
187
191
|
generation: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.number),
|
|
@@ -326,6 +330,7 @@ const orchestrationPanelOverviewSchema = boundaryDecoder_1.boundary.object({
|
|
|
326
330
|
initialized: boundaryDecoder_1.boundary.boolean,
|
|
327
331
|
lastActivityAt: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.nonEmptyString),
|
|
328
332
|
missing: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.boolean),
|
|
333
|
+
report: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.object({ ...agentReportFields, data: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.jsonObject) })),
|
|
329
334
|
});
|
|
330
335
|
const orchestrationPaneOverviewSchema = boundaryDecoder_1.boundary.object({
|
|
331
336
|
paneId: boundaryDecoder_1.boundary.nonEmptyString,
|
|
@@ -489,7 +494,6 @@ const paneCostResultSchema = boundaryDecoder_1.boundary.object({
|
|
|
489
494
|
});
|
|
490
495
|
exports.paneCreateResultSchema = boundaryDecoder_1.boundary.object({
|
|
491
496
|
ok: boundaryDecoder_1.boundary.boolean,
|
|
492
|
-
generation: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.number),
|
|
493
497
|
repo: repoSummarySchema,
|
|
494
498
|
items: boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.union(boundaryDecoder_1.boundary.object({
|
|
495
499
|
ok: boundaryDecoder_1.boundary.boolean,
|
|
@@ -516,7 +520,9 @@ exports.paneCreateResultSchema = boundaryDecoder_1.boundary.object({
|
|
|
516
520
|
error: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
517
521
|
})),
|
|
518
522
|
promptFile: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
523
|
+
attachments: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.string)),
|
|
519
524
|
warnings: promptWarningsSchema,
|
|
525
|
+
expectedReport: boundaryDecoder_1.boundary.optional(expectedReportSchema),
|
|
520
526
|
setupQueue: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.object({
|
|
521
527
|
position: boundaryDecoder_1.boundary.number,
|
|
522
528
|
message: boundaryDecoder_1.boundary.string,
|
|
@@ -540,6 +546,8 @@ exports.paneCreateResultSchema = boundaryDecoder_1.boundary.object({
|
|
|
540
546
|
code: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
541
547
|
}),
|
|
542
548
|
}))),
|
|
549
|
+
epoch: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
550
|
+
generation: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.number),
|
|
543
551
|
});
|
|
544
552
|
const paneArchiveResultSchema = boundaryDecoder_1.boundary.union(boundaryDecoder_1.boundary.object({
|
|
545
553
|
ok: boundaryDecoder_1.boundary.literal(false),
|
|
@@ -681,7 +689,6 @@ const panelResumeResultSchema = boundaryDecoder_1.boundary.object({
|
|
|
681
689
|
});
|
|
682
690
|
exports.panelCreateResultSchema = boundaryDecoder_1.boundary.object({
|
|
683
691
|
ok: boundaryDecoder_1.boundary.boolean,
|
|
684
|
-
generation: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.number),
|
|
685
692
|
paneId: boundaryDecoder_1.boundary.string,
|
|
686
693
|
panelId: boundaryDecoder_1.boundary.string,
|
|
687
694
|
title: boundaryDecoder_1.boundary.string,
|
|
@@ -695,8 +702,12 @@ exports.panelCreateResultSchema = boundaryDecoder_1.boundary.object({
|
|
|
695
702
|
readiness: boundaryDecoder_1.boundary.optional(panelReadinessSchema),
|
|
696
703
|
initialInput: boundaryDecoder_1.boundary.optional(initialInputSchema),
|
|
697
704
|
promptFile: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
705
|
+
attachments: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.string)),
|
|
698
706
|
warnings: promptWarningsSchema,
|
|
707
|
+
expectedReport: boundaryDecoder_1.boundary.optional(expectedReportSchema),
|
|
699
708
|
nextCommand: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
709
|
+
epoch: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
710
|
+
generation: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.number),
|
|
700
711
|
});
|
|
701
712
|
const panelOutputResultSchema = boundaryDecoder_1.boundary.object({
|
|
702
713
|
ok: boundaryDecoder_1.boundary.literal(true),
|
|
@@ -740,7 +751,6 @@ exports.panelScreenResultSchema = boundaryDecoder_1.boundary.object({
|
|
|
740
751
|
});
|
|
741
752
|
exports.panelSubmitResultSchema = boundaryDecoder_1.boundary.object({
|
|
742
753
|
ok: boundaryDecoder_1.boundary.boolean,
|
|
743
|
-
generation: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.number),
|
|
744
754
|
panelId: boundaryDecoder_1.boundary.string,
|
|
745
755
|
paneId: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
746
756
|
inputBytes: boundaryDecoder_1.boundary.number,
|
|
@@ -752,8 +762,12 @@ exports.panelSubmitResultSchema = boundaryDecoder_1.boundary.object({
|
|
|
752
762
|
sentAt: boundaryDecoder_1.boundary.string,
|
|
753
763
|
blocked: boundaryDecoder_1.boundary.optional(panelBlockedSchema),
|
|
754
764
|
promptFile: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
765
|
+
attachments: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.string)),
|
|
755
766
|
warnings: promptWarningsSchema,
|
|
767
|
+
expectedReport: boundaryDecoder_1.boundary.optional(expectedReportSchema),
|
|
756
768
|
nextCommand: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
769
|
+
epoch: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
770
|
+
generation: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.number),
|
|
757
771
|
});
|
|
758
772
|
const panelSubmitComposerResultSchema = boundaryDecoder_1.boundary.object({
|
|
759
773
|
ok: boundaryDecoder_1.boundary.boolean,
|
|
@@ -805,7 +819,7 @@ const agentDoctorResultSchema = boundaryDecoder_1.boundary.object({
|
|
|
805
819
|
})),
|
|
806
820
|
warnings: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.string)),
|
|
807
821
|
});
|
|
808
|
-
const workspaceEntryKindSchema = boundaryDecoder_1.boundary.enumeration('agent.ready', 'agent.busy', 'agent.blocked', 'agent.unknown', 'agent.idle', 'pane.created', 'pane.gone', 'panel.exited', 'panel.stopped', 'pane.associated', 'pane.detached', 'pr.conflicted', 'pr.checks', 'pr.merged', 'pr.closed', 'pr.head', 'pr.review', 'pr.comment', 'pr.stopped', 'github.unavailable', 'github.recovered', 'agent.report');
|
|
822
|
+
const workspaceEntryKindSchema = boundaryDecoder_1.boundary.enumeration('agent.ready', 'agent.busy', 'agent.blocked', 'agent.unknown', 'agent.idle', 'pane.created', 'pane.gone', 'panel.exited', 'panel.stopped', 'pane.associated', 'pane.detached', 'pr.conflicted', 'pr.checks', 'pr.merged', 'pr.closed', 'pr.head', 'pr.base_changed', 'pr.base_deleted', 'pr.review', 'pr.comment', 'pr.stopped', 'github.unavailable', 'github.recovered', 'agent.report', 'agent.report.missing');
|
|
809
823
|
const agentStateSchema = boundaryDecoder_1.boundary.enumeration('blocked', 'working', 'idle', 'unknown');
|
|
810
824
|
const workspacePanelSummarySchema = boundaryDecoder_1.boundary.object({
|
|
811
825
|
panelId: boundaryDecoder_1.boundary.string,
|
|
@@ -843,10 +857,15 @@ const workspaceEntrySchema = boundaryDecoder_1.boundary.object({
|
|
|
843
857
|
sessionId: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
844
858
|
sessionName: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
845
859
|
pr: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.object({ number: boundaryDecoder_1.boundary.number, url: boundaryDecoder_1.boundary.string, headOid: boundaryDecoder_1.boundary.string })),
|
|
860
|
+
base: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.object({ from: boundaryDecoder_1.boundary.string, to: boundaryDecoder_1.boundary.string })),
|
|
846
861
|
checks: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.enumeration('passed', 'failed')),
|
|
847
862
|
failingChecks: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.string)),
|
|
848
863
|
report: boundaryDecoder_1.boundary.optional(agentReportSchema),
|
|
849
864
|
panels: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.array(workspacePanelSummarySchema)),
|
|
865
|
+
lastOutput: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
866
|
+
lastOutputSource: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.enumeration('transcript', 'screen')),
|
|
867
|
+
expectedSince: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
868
|
+
screenLines: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.string)),
|
|
850
869
|
});
|
|
851
870
|
exports.workspaceStateResultSchema = boundaryDecoder_1.boundary.object({
|
|
852
871
|
ok: boundaryDecoder_1.boundary.literal(true),
|
|
@@ -1048,7 +1067,11 @@ async function runSessionsOverview(parsed) {
|
|
|
1048
1067
|
for (const pane of result.panes) {
|
|
1049
1068
|
const details = pane.missing ? 'missing' : pane.archived ? 'archived' : pane.panels.map(panel => `${panel.title}=${panel.state}`).join(', ') || 'no terminal panels';
|
|
1050
1069
|
console.log(` ${pane.name}: ${details}`);
|
|
1051
|
-
if (pane.report) {
|
|
1070
|
+
if (pane.panels.some(panel => panel.report)) {
|
|
1071
|
+
for (const panel of pane.panels)
|
|
1072
|
+
console.log(` ${describePanelOverview(panel)}`);
|
|
1073
|
+
}
|
|
1074
|
+
else if (pane.report) {
|
|
1052
1075
|
console.log(` report ${(0, watchLines_1.describeReport)(pane.report)} (panel ${pane.report.panelId}, ${pane.report.reportedAt})`);
|
|
1053
1076
|
}
|
|
1054
1077
|
}
|
|
@@ -1057,6 +1080,11 @@ async function runSessionsOverview(parsed) {
|
|
|
1057
1080
|
}
|
|
1058
1081
|
return 0;
|
|
1059
1082
|
}
|
|
1083
|
+
/** `Reviewer (panel <id>): idle, report ready pr#747 fc5dce9 at <reportedAt>`; a panel without a report shows only its state. */
|
|
1084
|
+
function describePanelOverview(panel) {
|
|
1085
|
+
const line = `${panel.title} (panel ${panel.panelId}): ${panel.state}`;
|
|
1086
|
+
return panel.report ? `${line}, report ${(0, watchLines_1.describeReport)(panel.report)} at ${panel.report.reportedAt}` : line;
|
|
1087
|
+
}
|
|
1060
1088
|
/** One daemon call waits at most this long; longer waits chain calls, each blocking in the daemon. */
|
|
1061
1089
|
const LOCK_WAIT_PER_CALL_MS = 120_000;
|
|
1062
1090
|
async function runLockAcquire(parsed) {
|
|
@@ -1220,7 +1248,7 @@ async function runPrWatchCommand(parsed) {
|
|
|
1220
1248
|
printJson(result);
|
|
1221
1249
|
return 0;
|
|
1222
1250
|
}
|
|
1223
|
-
async function runWatch(parsed) {
|
|
1251
|
+
async function runWatch(parsed, dispatch) {
|
|
1224
1252
|
if (parsed.watchAs && parsed.watchSince !== undefined) {
|
|
1225
1253
|
throw new Error('runpane watch accepts either --as or --since, not both.');
|
|
1226
1254
|
}
|
|
@@ -1299,6 +1327,10 @@ async function runWatch(parsed) {
|
|
|
1299
1327
|
return 0;
|
|
1300
1328
|
}
|
|
1301
1329
|
if (deadlineAt !== undefined && Date.now() >= deadlineAt) {
|
|
1330
|
+
if (dispatch) {
|
|
1331
|
+
printDispatchResult({ ok: false, outcome: 'timeout', ...dispatch, generation: request.since ?? 0 });
|
|
1332
|
+
return 124;
|
|
1333
|
+
}
|
|
1302
1334
|
emitControlLine((0, watchLines_1.formatNonEntry)('_timeout', { timeoutMs: parsed.timeoutMs }, format));
|
|
1303
1335
|
return 124;
|
|
1304
1336
|
}
|
|
@@ -1329,7 +1361,8 @@ async function runWatch(parsed) {
|
|
|
1329
1361
|
throw new Error('This Pane daemon does not support runpane watch --session; update Pane, or pass one --pane per Session Pane.');
|
|
1330
1362
|
}
|
|
1331
1363
|
if (failingCode) {
|
|
1332
|
-
|
|
1364
|
+
if (!dispatch)
|
|
1365
|
+
emitControlLine((0, watchLines_1.formatNonEntry)('_reconnected', { generation: result.generation }, format));
|
|
1333
1366
|
failingCode = undefined;
|
|
1334
1367
|
}
|
|
1335
1368
|
if (!armed && (keepsWaiting || parsed.selfTest)) {
|
|
@@ -1344,11 +1377,27 @@ async function runWatch(parsed) {
|
|
|
1344
1377
|
if (parsed.untilEvent) {
|
|
1345
1378
|
// A reset restates current state; it never ends the wait. Nor does the caller's own panel.
|
|
1346
1379
|
if (result.reset || result.dropped !== undefined) {
|
|
1380
|
+
if (dispatch) {
|
|
1381
|
+
printDispatchResult({ ok: false, outcome: 'indeterminate', ...dispatch, generation: result.generation,
|
|
1382
|
+
reason: result.reset?.reason ?? 'history-dropped',
|
|
1383
|
+
nextCommand: `runpane panels screen --panel ${dispatch.panelId ?? '<panel-id>'} --json; runpane inbox --json` });
|
|
1384
|
+
return 6;
|
|
1385
|
+
}
|
|
1347
1386
|
for (const line of (0, watchLines_1.formatWaitResult)(result, format))
|
|
1348
1387
|
emitControlLine(line);
|
|
1349
1388
|
}
|
|
1350
1389
|
else {
|
|
1351
|
-
|
|
1390
|
+
if (dispatch) {
|
|
1391
|
+
const entry = result.entries.find(candidate => !candidate.replay && !candidate.baseline
|
|
1392
|
+
&& isDispatchOutcomeKind(candidate.kind)
|
|
1393
|
+
&& (candidate.kind === 'pane.gone' || !dispatch.panelId || candidate.panelId === dispatch.panelId));
|
|
1394
|
+
if (entry && isDispatchOutcomeKind(entry.kind)) {
|
|
1395
|
+
const { outcome, exitCode } = DISPATCH_OUTCOMES[entry.kind];
|
|
1396
|
+
printDispatchResult(compactDispatchResult(outcome, entry));
|
|
1397
|
+
return exitCode;
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
const entries = dispatch ? [] : ownPanelId ? result.entries.filter(entry => entry.panelId !== ownPanelId) : result.entries;
|
|
1352
1401
|
if (entries.length > 0) {
|
|
1353
1402
|
for (const line of (0, watchLines_1.formatWaitResult)({ ...result, entries }, format))
|
|
1354
1403
|
emitWatchLine(line);
|
|
@@ -1383,7 +1432,8 @@ async function runWatch(parsed) {
|
|
|
1383
1432
|
}
|
|
1384
1433
|
const code = watchErrorCode(watchError);
|
|
1385
1434
|
if (failingCode !== code || heartbeatMs > 0 && Date.now() - lastFailureAt >= heartbeatMs) {
|
|
1386
|
-
|
|
1435
|
+
if (!dispatch)
|
|
1436
|
+
emitWatchLine((0, watchLines_1.formatNonEntry)('_error', { code, message: watchError.message }, format));
|
|
1387
1437
|
lastFailureAt = Date.now();
|
|
1388
1438
|
}
|
|
1389
1439
|
failingCode = code;
|
|
@@ -1446,11 +1496,26 @@ function isRetryableWatchError(error) {
|
|
|
1446
1496
|
}
|
|
1447
1497
|
async function runPanesCreate(parsed) {
|
|
1448
1498
|
const request = await buildPaneCreateRequest(parsed);
|
|
1499
|
+
if (parsed.waitForResult && request.panes.length !== 1)
|
|
1500
|
+
throw new errors_1.RunpaneError('ERR_RUNPANE_WAIT_MULTI_PANE', 'panes create --wait accepts one Pane, but this request has several. No workers were created.', 'Create several without --wait, then dispatch each with panels submit --wait.');
|
|
1501
|
+
if (parsed.expectReport !== undefined)
|
|
1502
|
+
request.expectReport = parsed.expectReport;
|
|
1449
1503
|
await confirmPaneCreate(parsed, request);
|
|
1450
1504
|
const result = await (0, daemonClient_1.invokeDaemon)('runpane:panes:create', [request], exports.paneCreateResultSchema, {
|
|
1451
1505
|
paneDir: parsed.paneDir,
|
|
1452
1506
|
timeoutMs: (parsed.timeoutMs ?? 120_000) + (parsed.readyTimeoutMs ?? 30_000) + 10_000,
|
|
1453
1507
|
});
|
|
1508
|
+
if (parsed.waitForResult) {
|
|
1509
|
+
const [item] = result.items;
|
|
1510
|
+
if (result.items.length !== 1) {
|
|
1511
|
+
printJson(result);
|
|
1512
|
+
throw new errors_1.RunpaneError('ERR_RUNPANE_WAIT_CREATE_RESULTS', 'Pane returned an unexpected number of creation results; the creation results are printed above.', 'Inspect the returned ids before retrying.');
|
|
1513
|
+
}
|
|
1514
|
+
// A Pane queued behind setup has no panel yet; its first agent panel's events still name the Pane.
|
|
1515
|
+
if (result.ok && item && 'paneId' in item && item.paneId) {
|
|
1516
|
+
return waitForDispatchResult(parsed, { paneId: item.paneId, panelId: item.panelId, since: result.generation, epoch: result.epoch });
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1454
1519
|
if (parsed.json) {
|
|
1455
1520
|
printJson(result);
|
|
1456
1521
|
}
|
|
@@ -1661,11 +1726,16 @@ async function runPanelsList(parsed) {
|
|
|
1661
1726
|
}
|
|
1662
1727
|
async function runPanelsCreate(parsed) {
|
|
1663
1728
|
const request = await buildPanelCreateRequest(parsed);
|
|
1729
|
+
if (parsed.expectReport !== undefined)
|
|
1730
|
+
request.expectReport = parsed.expectReport;
|
|
1664
1731
|
await confirmPanelCreate(parsed, request);
|
|
1665
1732
|
const result = await (0, daemonClient_1.invokeDaemon)('runpane:panels:create', [request], exports.panelCreateResultSchema, {
|
|
1666
1733
|
paneDir: parsed.paneDir,
|
|
1667
1734
|
timeoutMs: (parsed.readyTimeoutMs ?? 30_000) + 10_000,
|
|
1668
1735
|
});
|
|
1736
|
+
if (parsed.waitForResult && result.ok) {
|
|
1737
|
+
return waitForDispatchResult(parsed, { paneId: result.paneId, panelId: result.panelId, since: result.generation, epoch: result.epoch });
|
|
1738
|
+
}
|
|
1669
1739
|
if (parsed.json) {
|
|
1670
1740
|
printJson(result);
|
|
1671
1741
|
}
|
|
@@ -1852,12 +1922,115 @@ function describeReportDelivery(result) {
|
|
|
1852
1922
|
+ 'and a watch with a named cursor (runpane watch --as <name> --kinds agent.report) still receives it when it next waits.');
|
|
1853
1923
|
return lines;
|
|
1854
1924
|
}
|
|
1925
|
+
const inboxItemSchema = boundaryDecoder_1.boundary.object({
|
|
1926
|
+
id: boundaryDecoder_1.boundary.string,
|
|
1927
|
+
sessionId: boundaryDecoder_1.boundary.string,
|
|
1928
|
+
kind: boundaryDecoder_1.boundary.enumeration('report', 'report.missing', 'blocked'),
|
|
1929
|
+
at: boundaryDecoder_1.boundary.string,
|
|
1930
|
+
paneId: boundaryDecoder_1.boundary.string,
|
|
1931
|
+
paneName: boundaryDecoder_1.boundary.string,
|
|
1932
|
+
panelId: boundaryDecoder_1.boundary.string,
|
|
1933
|
+
panelTitle: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
1934
|
+
agentType: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
1935
|
+
report: boundaryDecoder_1.boundary.optional(agentReportSchema),
|
|
1936
|
+
lastOutput: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
1937
|
+
lastOutputSource: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.enumeration('transcript', 'screen')),
|
|
1938
|
+
reason: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.string),
|
|
1939
|
+
screenLines: boundaryDecoder_1.boundary.optional(boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.string)),
|
|
1940
|
+
});
|
|
1941
|
+
const inboxListResultSchema = boundaryDecoder_1.boundary.object({
|
|
1942
|
+
ok: boundaryDecoder_1.boundary.literal(true),
|
|
1943
|
+
session: boundaryDecoder_1.boundary.object({ id: boundaryDecoder_1.boundary.string, name: boundaryDecoder_1.boundary.string }),
|
|
1944
|
+
items: boundaryDecoder_1.boundary.array(inboxItemSchema),
|
|
1945
|
+
});
|
|
1946
|
+
const inboxAckResultSchema = boundaryDecoder_1.boundary.object({
|
|
1947
|
+
ok: boundaryDecoder_1.boundary.literal(true),
|
|
1948
|
+
acknowledged: boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.string),
|
|
1949
|
+
unknown: boundaryDecoder_1.boundary.array(boundaryDecoder_1.boundary.string),
|
|
1950
|
+
});
|
|
1951
|
+
const MAX_INBOX_DETAIL_LENGTH = 300;
|
|
1952
|
+
/** `--session`, else the Session whose orchestrator this terminal is. */
|
|
1953
|
+
function inboxSession(parsed, env) {
|
|
1954
|
+
return parsed.sessionId?.trim() || env.PANE_ORCHESTRATION_SESSION_ID?.trim() || undefined;
|
|
1955
|
+
}
|
|
1956
|
+
async function runInbox(parsed, env = process.env) {
|
|
1957
|
+
const session = inboxSession(parsed, env);
|
|
1958
|
+
if (!session) {
|
|
1959
|
+
throw new Error('runpane inbox needs --session <id|name> outside a Session orchestrator (PANE_ORCHESTRATION_SESSION_ID).');
|
|
1960
|
+
}
|
|
1961
|
+
const result = await (0, daemonClient_1.invokeDaemon)('runpane:inbox:list', [{ session }], inboxListResultSchema, { paneDir: parsed.paneDir });
|
|
1962
|
+
if (parsed.json) {
|
|
1963
|
+
printJson(result);
|
|
1964
|
+
return 0;
|
|
1965
|
+
}
|
|
1966
|
+
if (result.items.length === 0) {
|
|
1967
|
+
console.log(`Inbox for ${result.session.name}: nothing unacknowledged.`);
|
|
1968
|
+
return 0;
|
|
1969
|
+
}
|
|
1970
|
+
console.log(`Inbox for ${result.session.name}: ${result.items.length} unacknowledged.`);
|
|
1971
|
+
for (const item of result.items)
|
|
1972
|
+
console.log(formatInboxItem(item));
|
|
1973
|
+
console.log(`Clear handled items with: runpane inbox ack ${result.items.map(item => item.id).join(' ')}`);
|
|
1974
|
+
return 0;
|
|
1975
|
+
}
|
|
1976
|
+
async function runInboxAck(parsed, env = process.env) {
|
|
1977
|
+
const session = parsed.all ? inboxSession(parsed, env) : parsed.sessionId;
|
|
1978
|
+
if (parsed.all && !session) {
|
|
1979
|
+
throw new Error('runpane inbox ack --all needs --session <id|name> outside a Session orchestrator.');
|
|
1980
|
+
}
|
|
1981
|
+
const result = await (0, daemonClient_1.invokeDaemon)('runpane:inbox:ack', [{
|
|
1982
|
+
ids: parsed.all ? undefined : parsed.inboxIds,
|
|
1983
|
+
session,
|
|
1984
|
+
all: parsed.all || undefined,
|
|
1985
|
+
}], inboxAckResultSchema, { paneDir: parsed.paneDir });
|
|
1986
|
+
if (parsed.json) {
|
|
1987
|
+
printJson(result);
|
|
1988
|
+
}
|
|
1989
|
+
else {
|
|
1990
|
+
console.log(`Acknowledged ${result.acknowledged.length} inbox item${result.acknowledged.length === 1 ? '' : 's'}.`);
|
|
1991
|
+
if (result.unknown.length > 0)
|
|
1992
|
+
console.log(`Not found (already acknowledged?): ${result.unknown.join(', ')}`);
|
|
1993
|
+
}
|
|
1994
|
+
return 0;
|
|
1995
|
+
}
|
|
1996
|
+
/** `<id> REPORT <pane> pane <id> panel <id> ready pr#12 abc1234`, then indented detail lines. */
|
|
1997
|
+
function formatInboxItem(item) {
|
|
1998
|
+
const where = `${oneLine(item.paneName)} pane ${item.paneId} panel ${item.panelId}`;
|
|
1999
|
+
if (item.kind === 'report') {
|
|
2000
|
+
const lines = [`${item.id} REPORT ${where} ${item.report ? (0, watchLines_1.describeReport)(item.report) : 'unknown'}`];
|
|
2001
|
+
if (item.report?.summary)
|
|
2002
|
+
lines.push(` ${clip(oneLine(item.report.summary))}`);
|
|
2003
|
+
return lines.join('\n');
|
|
2004
|
+
}
|
|
2005
|
+
if (item.kind === 'report.missing') {
|
|
2006
|
+
const lines = [`${item.id} MISSING REPORT ${where} ${item.reason ?? 'ready'} without runpane report`];
|
|
2007
|
+
if (item.lastOutput)
|
|
2008
|
+
lines.push(` last ${item.lastOutputSource ?? 'output'}: ${clip(oneLine(item.lastOutput), true)}`);
|
|
2009
|
+
return lines.join('\n');
|
|
2010
|
+
}
|
|
2011
|
+
const lines = [`${item.id} BLOCKED ${where} waiting on a prompt`];
|
|
2012
|
+
for (const line of (item.screenLines ?? []).slice(-8))
|
|
2013
|
+
lines.push(` | ${line}`);
|
|
2014
|
+
return lines.join('\n');
|
|
2015
|
+
}
|
|
2016
|
+
function oneLine(text) {
|
|
2017
|
+
return text.replace(/\s+/gu, ' ').trim();
|
|
2018
|
+
}
|
|
2019
|
+
/** Cut to the detail length, keeping the start, or the end when `tail` (an agent's conclusion is last). */
|
|
2020
|
+
function clip(text, tail = false) {
|
|
2021
|
+
if (text.length <= MAX_INBOX_DETAIL_LENGTH)
|
|
2022
|
+
return text;
|
|
2023
|
+
return tail ? `…${text.slice(text.length - MAX_INBOX_DETAIL_LENGTH + 1)}` : `${text.slice(0, MAX_INBOX_DETAIL_LENGTH - 1)}…`;
|
|
2024
|
+
}
|
|
1855
2025
|
async function runPanelsSubmit(parsed) {
|
|
1856
2026
|
const request = buildPanelInputRequest(parsed, 'submit');
|
|
1857
2027
|
await confirmPanelInput(parsed, request, 'submit');
|
|
1858
2028
|
const result = await (0, daemonClient_1.invokeDaemon)('runpane:panels:submit', [request], exports.panelSubmitResultSchema, {
|
|
1859
2029
|
paneDir: parsed.paneDir,
|
|
1860
2030
|
});
|
|
2031
|
+
if (parsed.waitForResult && result.ok) {
|
|
2032
|
+
return waitForDispatchResult(parsed, { paneId: result.paneId, panelId: result.panelId, since: result.generation, epoch: result.epoch });
|
|
2033
|
+
}
|
|
1861
2034
|
if (parsed.json) {
|
|
1862
2035
|
printJson(result);
|
|
1863
2036
|
}
|
|
@@ -1876,6 +2049,64 @@ async function runPanelsSubmit(parsed) {
|
|
|
1876
2049
|
}
|
|
1877
2050
|
return result.ok ? 0 : 1;
|
|
1878
2051
|
}
|
|
2052
|
+
/** Each way a `--wait` dispatch ends, the journal kind behind it, and the exit code it returns. */
|
|
2053
|
+
const DISPATCH_OUTCOMES = {
|
|
2054
|
+
'agent.report': { outcome: 'report', exitCode: 0 },
|
|
2055
|
+
'agent.report.missing': { outcome: 'missing', exitCode: 3 },
|
|
2056
|
+
'agent.blocked': { outcome: 'blocked', exitCode: 4 },
|
|
2057
|
+
'panel.exited': { outcome: 'exited', exitCode: 5 },
|
|
2058
|
+
'pane.gone': { outcome: 'gone', exitCode: 5 },
|
|
2059
|
+
};
|
|
2060
|
+
const DISPATCH_WAIT_KINDS = ['agent.report', 'agent.report.missing', 'agent.blocked', 'panel.exited', 'pane.gone'];
|
|
2061
|
+
function isDispatchOutcomeKind(kind) {
|
|
2062
|
+
return Object.hasOwn(DISPATCH_OUTCOMES, kind);
|
|
2063
|
+
}
|
|
2064
|
+
/**
|
|
2065
|
+
* `--wait`: block until the dispatched panel reports, misses its report, waits on a prompt, or
|
|
2066
|
+
* exits, then print the result as one line of JSON. `since` is the journal generation the daemon took
|
|
2067
|
+
* before delivering the work, so an event right after delivery is never missed.
|
|
2068
|
+
*/
|
|
2069
|
+
async function waitForDispatchResult(parsed, target) {
|
|
2070
|
+
if (!target.paneId || target.since === undefined) {
|
|
2071
|
+
throw new Error('This Pane daemon does not return a journal position, so --wait cannot follow the work. Update Pane, or use runpane watch --until-event.');
|
|
2072
|
+
}
|
|
2073
|
+
return runWatch({ ...parsed, follow: false, untilEvent: true,
|
|
2074
|
+
watchAs: undefined, watchFrom: undefined, watchSince: target.since, watchEpoch: target.epoch,
|
|
2075
|
+
watchKinds: DISPATCH_WAIT_KINDS, watchPaneIds: [target.paneId],
|
|
2076
|
+
sessionId: undefined, repo: undefined, includeShells: true, noHeldInput: true,
|
|
2077
|
+
timeoutMs: parsed.waitTimeoutMs || undefined,
|
|
2078
|
+
}, { paneId: target.paneId, panelId: target.panelId });
|
|
2079
|
+
}
|
|
2080
|
+
function printDispatchResult(result) {
|
|
2081
|
+
console.log(JSON.stringify(result));
|
|
2082
|
+
}
|
|
2083
|
+
/** The `--wait` result: what the orchestrator needs to act, never the transcript. */
|
|
2084
|
+
function compactDispatchResult(outcome, entry) {
|
|
2085
|
+
const { report } = entry;
|
|
2086
|
+
return {
|
|
2087
|
+
ok: outcome === 'report',
|
|
2088
|
+
outcome,
|
|
2089
|
+
paneId: entry.paneId,
|
|
2090
|
+
paneName: entry.paneName,
|
|
2091
|
+
generation: entry.gen,
|
|
2092
|
+
at: entry.at,
|
|
2093
|
+
panelId: entry.panelId,
|
|
2094
|
+
report: report && {
|
|
2095
|
+
state: report.state,
|
|
2096
|
+
pr: report.pr,
|
|
2097
|
+
head: report.head,
|
|
2098
|
+
summary: report.summary,
|
|
2099
|
+
summaryTruncated: report.summaryTruncated,
|
|
2100
|
+
summaryPath: report.summaryPath,
|
|
2101
|
+
question: report.question,
|
|
2102
|
+
},
|
|
2103
|
+
screenLines: entry.screenLines?.length ? entry.screenLines : undefined,
|
|
2104
|
+
lastOutput: entry.lastOutput || undefined,
|
|
2105
|
+
lastOutputSource: entry.lastOutput ? entry.lastOutputSource : undefined,
|
|
2106
|
+
reason: entry.reason || undefined,
|
|
2107
|
+
exitCode: entry.exitCode,
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
1879
2110
|
/** Where the prompt went, for human output: `Delivery: queued (transcript)`. */
|
|
1880
2111
|
function printDelivery(delivery, prefix = '') {
|
|
1881
2112
|
if (delivery) {
|
|
@@ -1907,6 +2138,12 @@ function printPromptNotes(result, prefix = '') {
|
|
|
1907
2138
|
if (result.promptFile) {
|
|
1908
2139
|
console.log(`${prefix}Prompt file: ${result.promptFile}`);
|
|
1909
2140
|
}
|
|
2141
|
+
for (const attachment of result.attachments ?? []) {
|
|
2142
|
+
console.log(`${prefix}Attached: ${attachment}`);
|
|
2143
|
+
}
|
|
2144
|
+
if (result.expectedReport) {
|
|
2145
|
+
console.log(`${prefix}Expecting a runpane report; without one Pane emits agent.report.missing.`);
|
|
2146
|
+
}
|
|
1910
2147
|
for (const warning of result.warnings ?? []) {
|
|
1911
2148
|
console.log(`${prefix}Warning (${warning.code}): ${warning.message}`);
|
|
1912
2149
|
}
|
|
@@ -2038,8 +2275,10 @@ function buildPanelInputRequest(parsed, command = 'input') {
|
|
|
2038
2275
|
panelId: parsed.panelId,
|
|
2039
2276
|
input: parsed.keys ? keysToBytes(parsed.keys) : parsed.panelInputFile ? readInputSource(parsed.panelInputFile) : parsed.panelInput ?? '',
|
|
2040
2277
|
asFilePointer: parsed.asFilePointer || undefined,
|
|
2278
|
+
attachments: resolveAttachments(parsed, `panels ${command}`),
|
|
2041
2279
|
interrupt: parsed.interrupt || undefined,
|
|
2042
2280
|
source: parsed.source === 'user' || parsed.source === 'agent' ? parsed.source : undefined,
|
|
2281
|
+
expectReport: command === 'submit' ? parsed.expectReport : undefined,
|
|
2043
2282
|
};
|
|
2044
2283
|
}
|
|
2045
2284
|
/** `--keys down,enter`: named keys from the contract, or a single literal character each. */
|
|
@@ -2187,6 +2426,7 @@ async function buildToolSpec(parsed, command = 'panes create') {
|
|
|
2187
2426
|
throw new Error(`--as-file-pointer needs a prompt: pass --prompt or --initial-input-file to runpane ${command}.`);
|
|
2188
2427
|
}
|
|
2189
2428
|
const initialInputAsFilePointer = parsed.asFilePointer || undefined;
|
|
2429
|
+
const initialInputAttachments = resolveAttachments(parsed, command);
|
|
2190
2430
|
// With --tool-command, --agent names the agent the command runs (a wrapper
|
|
2191
2431
|
// such as `agent-farm run`); Pane launches the command unchanged.
|
|
2192
2432
|
if (parsed.toolCommand && parsed.agent) {
|
|
@@ -2196,6 +2436,7 @@ async function buildToolSpec(parsed, command = 'panes create') {
|
|
|
2196
2436
|
title: parsed.title,
|
|
2197
2437
|
initialInput,
|
|
2198
2438
|
initialInputAsFilePointer,
|
|
2439
|
+
initialInputAttachments,
|
|
2199
2440
|
};
|
|
2200
2441
|
}
|
|
2201
2442
|
let agent = parsed.agent;
|
|
@@ -2211,6 +2452,7 @@ async function buildToolSpec(parsed, command = 'panes create') {
|
|
|
2211
2452
|
title: parsed.title,
|
|
2212
2453
|
initialInput,
|
|
2213
2454
|
initialInputAsFilePointer,
|
|
2455
|
+
initialInputAttachments,
|
|
2214
2456
|
};
|
|
2215
2457
|
}
|
|
2216
2458
|
if (!parsed.toolCommand) {
|
|
@@ -2221,8 +2463,30 @@ async function buildToolSpec(parsed, command = 'panes create') {
|
|
|
2221
2463
|
title: parsed.title,
|
|
2222
2464
|
initialInput,
|
|
2223
2465
|
initialInputAsFilePointer,
|
|
2466
|
+
initialInputAttachments,
|
|
2224
2467
|
};
|
|
2225
2468
|
}
|
|
2469
|
+
/**
|
|
2470
|
+
* `--attach` files as absolute paths. Pane copies them beside the prompt file
|
|
2471
|
+
* (inside the worktree when it can), so they need `--as-file-pointer`.
|
|
2472
|
+
*/
|
|
2473
|
+
function resolveAttachments(parsed, command) {
|
|
2474
|
+
if (!parsed.attachments?.length)
|
|
2475
|
+
return undefined;
|
|
2476
|
+
if (!['panes create', 'panels create', 'panels submit'].includes(command)) {
|
|
2477
|
+
throw new errors_1.RunpaneError('ERR_ATTACH_UNSUPPORTED', `--attach is not supported by runpane ${command}. Nothing was sent.`, 'Use --attach with panes create, panels create or panels submit.');
|
|
2478
|
+
}
|
|
2479
|
+
if (!parsed.asFilePointer) {
|
|
2480
|
+
throw new errors_1.RunpaneError('ERR_ATTACH_NEEDS_FILE_POINTER', `--attach needs --as-file-pointer: Pane lists the copied files beside the prompt file. Nothing was sent.`, `Add --as-file-pointer to runpane ${command}.`);
|
|
2481
|
+
}
|
|
2482
|
+
return parsed.attachments.map((file) => {
|
|
2483
|
+
const absolute = node_path_1.default.resolve(file);
|
|
2484
|
+
if (!node_fs_1.default.statSync(absolute, { throwIfNoEntry: false })?.isFile()) {
|
|
2485
|
+
throw new errors_1.RunpaneError('ERR_ATTACH_NOT_FOUND', `--attach ${file}: no such file. Nothing was sent.`, 'Pass an existing file path, then retry.');
|
|
2486
|
+
}
|
|
2487
|
+
return absolute;
|
|
2488
|
+
});
|
|
2489
|
+
}
|
|
2226
2490
|
function resolveInitialInput(parsed) {
|
|
2227
2491
|
if (parsed.initialInput && parsed.initialInputFile) {
|
|
2228
2492
|
throw new Error('Use either --initial-input/--prompt or --initial-input-file/--prompt-file, not both.');
|
|
@@ -2499,12 +2763,15 @@ function workspaceLabel(kind) {
|
|
|
2499
2763
|
'pr.merged': 'PR MERGED',
|
|
2500
2764
|
'pr.closed': 'PR.CLOSED',
|
|
2501
2765
|
'pr.head': 'PR.HEAD',
|
|
2766
|
+
'pr.base_changed': 'PR.BASE_CHANGED',
|
|
2767
|
+
'pr.base_deleted': 'PR.BASE_DELETED',
|
|
2502
2768
|
'pr.review': 'PR.REVIEW',
|
|
2503
2769
|
'pr.comment': 'PR.COMMENT',
|
|
2504
2770
|
'pr.stopped': 'PR.STOPPED',
|
|
2505
2771
|
'github.unavailable': 'GITHUB.UNAVAILABLE',
|
|
2506
2772
|
'github.recovered': 'GITHUB.RECOVERED',
|
|
2507
2773
|
'agent.report': 'REPORT',
|
|
2774
|
+
'agent.report.missing': 'MISSING REPORT',
|
|
2508
2775
|
};
|
|
2509
2776
|
return labels[kind];
|
|
2510
2777
|
}
|