@detergent-software/conductor 1.4.0-dev.14 → 1.4.0-dev.15
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/CHANGELOG.md +7 -0
- package/mod/hooks/model.ts +16 -7
- package/mod/hooks/register.tsx +32 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Releases are tagged `conductor-v<version>` and staged on npm by `atk-conductor-release` (`pipelines/release-conductor.yml`), which writes each section below. `scripts/check-changelog.mjs` fails when a tag has no section here.
|
|
4
4
|
|
|
5
|
+
## [1.4.0-dev.15](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.4.0-dev.14&targetVersion=GTconductor-v1.4.0-dev.15) (2026-10-15)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
* **conductor:** approve and revise from the console through conduct (ATK-508) ([f3f1785](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/f3f1785136e152ade05cad8cab8e938c5eefef08))
|
|
10
|
+
* **conductor:** say when an approve may have posted, on every path (ATK-508) ([8278f23](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/commit/8278f23a6adfe6a73904674802c6026a54bba983))
|
|
11
|
+
|
|
5
12
|
## [1.4.0-dev.14](https://emergentcode.visualstudio.com/Emergent%20Software/_git/Emergent.AgenticToolkit/branchCompare?baseVersion=GTconductor-v1.4.0-dev.13&targetVersion=GTconductor-v1.4.0-dev.14) (2026-10-15)
|
|
6
13
|
|
|
7
14
|
### Bug Fixes
|
package/mod/hooks/model.ts
CHANGED
|
@@ -383,9 +383,15 @@ export function errorOf(answer: ApiAnswer): string {
|
|
|
383
383
|
return `the conductor answered ${answer.status}`;
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
+
// What `conduct approve|revise` adds when the daemon can't say whether it posted the comment.
|
|
387
|
+
export const MAYBE_POSTED = 'the comment may be on the item already: look before answering again';
|
|
388
|
+
|
|
386
389
|
// POST /inbox/:id/resolve's answer as the line to show, as `conduct approve|revise|done` says it.
|
|
387
390
|
export function answerText(action: 'approve' | 'done' | 'revise', id: number, answer: ApiAnswer): string {
|
|
388
|
-
if (answer.status !== 200)
|
|
391
|
+
if (answer.status !== 200) {
|
|
392
|
+
const unknown = answer.status === 502 && isRecord(answer.body) && answer.body.posted === 'unknown';
|
|
393
|
+
return `not ${action === 'done' ? 'done' : `${action}d`}: ${errorOf(answer)}${unknown ? `; ${MAYBE_POSTED}` : ''}`;
|
|
394
|
+
}
|
|
389
395
|
const body = isRecord(answer.body) ? answer.body : {};
|
|
390
396
|
const key = isRecord(body.item) && typeof body.item.item === 'string' ? body.item.item : `#${id}`;
|
|
391
397
|
if (action === 'done') return `done: ${key}`;
|
|
@@ -395,23 +401,26 @@ export function answerText(action: 'approve' | 'done' | 'revise', id: number, an
|
|
|
395
401
|
return `${action === 'approve' ? 'approved' : 'asked for a revision of'} ${key}: ${where}`;
|
|
396
402
|
}
|
|
397
403
|
|
|
398
|
-
// A `conduct
|
|
399
|
-
//
|
|
400
|
-
// without anything node wrote before it (a warning).
|
|
401
|
-
export function
|
|
404
|
+
// A `conduct` run (attach, approve, revise) as the line to show: what it printed when it succeeded
|
|
405
|
+
// (`woke …`, `approved …`), else `failed`, then its error and details without the `conduct: `
|
|
406
|
+
// prefix, and without anything node wrote before it (a warning).
|
|
407
|
+
export function runText(
|
|
408
|
+
run: { exitCode: number; stderr: string; stdout: string },
|
|
409
|
+
says: { failed: string; ok: string },
|
|
410
|
+
): string {
|
|
402
411
|
const lines = (text: string): string[] =>
|
|
403
412
|
text
|
|
404
413
|
.split('\n')
|
|
405
414
|
.map((l) => l.trim())
|
|
406
415
|
.filter((l) => l !== '');
|
|
407
|
-
if (run.exitCode === 0) return lines(run.stdout).join('; ') ||
|
|
416
|
+
if (run.exitCode === 0) return lines(run.stdout).join('; ') || says.ok;
|
|
408
417
|
const stderr = lines(run.stderr);
|
|
409
418
|
const from = stderr.findIndex((l) => l.startsWith('conduct: '));
|
|
410
419
|
const error = stderr
|
|
411
420
|
.slice(Math.max(from, 0))
|
|
412
421
|
.map((l) => l.replace(/^conduct: /, ''))
|
|
413
422
|
.join('; ');
|
|
414
|
-
return
|
|
423
|
+
return `${says.failed}: ${error || `conduct exited ${run.exitCode}`}`;
|
|
415
424
|
}
|
|
416
425
|
|
|
417
426
|
// The daemon's home as the CLI finds it (apps/conductor/src/paths.ts): CONDUCTOR_HOME, else
|
package/mod/hooks/register.tsx
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
age,
|
|
8
8
|
type ApiAnswer,
|
|
9
9
|
answerText,
|
|
10
|
-
attachRunText,
|
|
11
10
|
bandEntries,
|
|
12
11
|
clearedText,
|
|
13
12
|
CONSOLE_SKILL,
|
|
@@ -20,6 +19,7 @@ import {
|
|
|
20
19
|
idleAction,
|
|
21
20
|
inboxText,
|
|
22
21
|
isWebLink,
|
|
22
|
+
MAYBE_POSTED,
|
|
23
23
|
NO_SKILL_CONTEXT,
|
|
24
24
|
NO_SKILL_WARNING,
|
|
25
25
|
notifyArgs,
|
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
pushWarning,
|
|
38
38
|
reaction,
|
|
39
39
|
reportedState,
|
|
40
|
+
runText,
|
|
40
41
|
snapshotOf,
|
|
41
42
|
spendToday,
|
|
42
43
|
statusText,
|
|
@@ -60,6 +61,9 @@ const CONFIRM_MS = 30_000;
|
|
|
60
61
|
// gives up on an answer not complete within 30 s (ATK-507). `conduct attach` waits that long, and
|
|
61
62
|
// $.process.run allows a command ten minutes at most.
|
|
62
63
|
const ATTACH_RUN_MS = 10 * 60_000;
|
|
64
|
+
// An approve or a revise asks Linear twice, and `conduct approve|revise` allows that 60 s (ATK-508);
|
|
65
|
+
// this adds node's start.
|
|
66
|
+
const ANSWER_RUN_MS = 2 * 60_000;
|
|
63
67
|
const NOT_RUNNING = 'the conductor is not running (conduct serve)';
|
|
64
68
|
|
|
65
69
|
const snapshot = atom({ plugin: 'conductor', key: 'snapshot' } as const, null);
|
|
@@ -363,13 +367,38 @@ async function follow($: EngineInterface): Promise<void> {
|
|
|
363
367
|
}
|
|
364
368
|
|
|
365
369
|
// Jason's answer to an inbox item, as `conduct approve|revise|done` posts it; the line to show.
|
|
370
|
+
// Approve and revise run `conduct` (ANSWER_RUN_MS), by inbox id. Without the CLI, the API, which
|
|
371
|
+
// $.http.fetch cuts off after 30 s, possibly after the comment was posted. Done doesn't ask Linear.
|
|
366
372
|
async function answer(
|
|
367
373
|
$: EngineInterface,
|
|
368
374
|
id: number,
|
|
369
375
|
body: { action: 'approve' | 'done' } | { action: 'revise'; text: string },
|
|
370
376
|
): Promise<string> {
|
|
371
377
|
try {
|
|
372
|
-
const
|
|
378
|
+
const cli = cliOf($);
|
|
379
|
+
if (body.action !== 'done' && (await $.fs.exists(cli))) {
|
|
380
|
+
const failed = `not ${body.action}d`;
|
|
381
|
+
const argv = body.action === 'approve' ? ['approve', `#${id}`] : ['revise', `#${id}`, body.text];
|
|
382
|
+
let run;
|
|
383
|
+
try {
|
|
384
|
+
run = await $.process.run(['node', cli, ...argv], { timeoutMs: ANSWER_RUN_MS });
|
|
385
|
+
} catch (error) {
|
|
386
|
+
return `${failed}: ${error instanceof Error ? error.message : String(error)}`;
|
|
387
|
+
}
|
|
388
|
+
refreshSoon($);
|
|
389
|
+
return runText(run, {
|
|
390
|
+
failed,
|
|
391
|
+
ok: `${body.action === 'approve' ? 'approved' : 'asked for a revision of'} #${id}`,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
const at = await endpoint($);
|
|
395
|
+
let result: ApiAnswer;
|
|
396
|
+
try {
|
|
397
|
+
result = await callApi($, 'POST', `/inbox/${id}/resolve`, body, at);
|
|
398
|
+
} catch (error) {
|
|
399
|
+
if (body.action === 'done') throw error;
|
|
400
|
+
return `not ${body.action}d: ${error instanceof Error ? error.message : String(error)}; ${MAYBE_POSTED}`;
|
|
401
|
+
}
|
|
373
402
|
refreshSoon($);
|
|
374
403
|
return answerText(body.action, id, result);
|
|
375
404
|
} catch (error) {
|
|
@@ -415,7 +444,7 @@ async function attach($: EngineInterface, id: number, name: string): Promise<str
|
|
|
415
444
|
$.ui.toast(`attaching to ${name}…`);
|
|
416
445
|
const run = await $.process.run(['node', cli, 'attach', `#${id}`], { timeoutMs: ATTACH_RUN_MS });
|
|
417
446
|
refreshSoon($);
|
|
418
|
-
return
|
|
447
|
+
return runText(run, { failed: `could not attach to ${name}`, ok: `attached to ${name}` });
|
|
419
448
|
}
|
|
420
449
|
const result = await callApi($, 'POST', `/workers/${id}/attach`);
|
|
421
450
|
refreshSoon($);
|
package/package.json
CHANGED