@dougbots/avenor-pi 0.8.3 → 0.10.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/README.md +35 -5
- package/dist/index.d.ts +101 -2
- package/dist/index.js +780 -45
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { Input, Key, Text, matchesKey, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "@earendil-works/pi-tui";
|
|
2
2
|
import { Type } from "typebox";
|
|
3
|
-
import { Supervisor, answerPermissionTool, dial, eventsTool, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool } from "@dougbots/avenor-core";
|
|
3
|
+
import { Supervisor, answerPermissionTool, dial, eventsTool, extractEventText, followUpTool, inspectTool, observeRun, resultTool, shutdownTool, socketsRoot, spawnTool, statusTool, validateSpawnSelection } from "@dougbots/avenor-core";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import * as path from "node:path";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
6
7
|
//#region src/watch.ts
|
|
7
8
|
const ANSI_PATTERN = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[a-zA-Z\d]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g;
|
|
8
9
|
const TRANSCRIPT_SCROLL_STEP = 4;
|
|
9
10
|
const ACTION_STATUS_CHARS = 180;
|
|
10
11
|
const PREVIEW_CHARS = 240;
|
|
11
12
|
const TOOL_PREVIEW_CHARS = 120;
|
|
12
|
-
const FINAL_OUTPUT_CHARS = 600;
|
|
13
|
+
const FINAL_OUTPUT_CHARS$1 = 600;
|
|
13
14
|
const TRANSCRIPT_PADDING = 2;
|
|
14
15
|
const INSPECT_ERROR_PREFIX = "error: ";
|
|
15
16
|
const TERMINAL_STATUSES$1 = new Set([
|
|
@@ -90,7 +91,7 @@ function usageSummary(usage) {
|
|
|
90
91
|
return parts.join(" · ");
|
|
91
92
|
}
|
|
92
93
|
function finalOutputPreview(status, snapshot) {
|
|
93
|
-
return clipText(status?.final_output ?? snapshot?.final_output ?? snapshot?.assistant_text, FINAL_OUTPUT_CHARS);
|
|
94
|
+
return clipText(status?.final_output ?? snapshot?.final_output ?? snapshot?.assistant_text, FINAL_OUTPUT_CHARS$1);
|
|
94
95
|
}
|
|
95
96
|
function currentStatusWord(status, snapshot) {
|
|
96
97
|
const raw = status?.status?.toLowerCase();
|
|
@@ -702,6 +703,542 @@ function resolveAgentProfile(entries, environment = process.env) {
|
|
|
702
703
|
return selected;
|
|
703
704
|
}
|
|
704
705
|
//#endregion
|
|
706
|
+
//#region src/events.ts
|
|
707
|
+
/**
|
|
708
|
+
* Public telemetry types and adapters for companion Pi extensions.
|
|
709
|
+
*
|
|
710
|
+
* The transport is Pi's shared `pi.events` bus. This module owns no event
|
|
711
|
+
* handlers or singleton state, so consumers can use the surface even when
|
|
712
|
+
* package dependencies are resolved from different module instances.
|
|
713
|
+
*
|
|
714
|
+
* Payloads select status fields and omit transcripts, final output, directories,
|
|
715
|
+
* socket paths, raw status objects, and tool arguments.
|
|
716
|
+
*/
|
|
717
|
+
/** Emitted when the official extension completes a polling cycle. */
|
|
718
|
+
const CHANNEL_POLL_COMPLETED = "avenor:poll:completed";
|
|
719
|
+
/** Emitted when the extension observes a terminal run result. */
|
|
720
|
+
const CHANNEL_RUN_TERMINAL = "avenor:run:terminal";
|
|
721
|
+
/** Emitted when a best-effort polling operation fails. */
|
|
722
|
+
const CHANNEL_POLL_ERROR = "avenor:poll:error";
|
|
723
|
+
/** Maximum sanitized error text exposed to companion extensions. */
|
|
724
|
+
const POLL_ERROR_TEXT_CHARS = 600;
|
|
725
|
+
function sanitizeAndBoundText(value) {
|
|
726
|
+
const text = sanitizeText(String(value)).trim();
|
|
727
|
+
if (!text) return "unknown error";
|
|
728
|
+
return text.length <= POLL_ERROR_TEXT_CHARS ? text : `${text.slice(0, POLL_ERROR_TEXT_CHARS - 1)}…`;
|
|
729
|
+
}
|
|
730
|
+
function createSupervisorKey(supervisorId) {
|
|
731
|
+
if (!supervisorId) return "singleton";
|
|
732
|
+
return `supervisor:${createHash("sha256").update(supervisorId).digest("hex").slice(0, 16)}`;
|
|
733
|
+
}
|
|
734
|
+
/** Emit an Avenor event with a type-checked payload. */
|
|
735
|
+
function emitAvenorEvent(bus, event, data) {
|
|
736
|
+
bus.emit(event, data);
|
|
737
|
+
}
|
|
738
|
+
/** Subscribe to an Avenor event and return Pi's unsubscribe function. */
|
|
739
|
+
function onAvenorEvent(bus, event, handler) {
|
|
740
|
+
return bus.on(event, handler);
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Copy and freeze the selected fields exposed to companion consumers.
|
|
744
|
+
* Keep this allowlist explicit so future private RunStatusEntry fields are not
|
|
745
|
+
* included in the event accidentally.
|
|
746
|
+
*/
|
|
747
|
+
function createPollCompletedPayload(entries, timestamp, generation) {
|
|
748
|
+
const publicEntries = entries.map((entry) => Object.freeze({
|
|
749
|
+
runId: entry.runId,
|
|
750
|
+
supervisorKey: createSupervisorKey(entry.supervisorId),
|
|
751
|
+
...entry.runtimeId !== void 0 && { runtimeId: entry.runtimeId },
|
|
752
|
+
label: entry.label,
|
|
753
|
+
status: entry.status,
|
|
754
|
+
...entry.phase !== void 0 && { phase: entry.phase },
|
|
755
|
+
...entry.phaseLabel !== void 0 && { phaseLabel: entry.phaseLabel },
|
|
756
|
+
agent: entry.agent,
|
|
757
|
+
...entry.pendingPermission !== void 0 && { pendingPermission: entry.pendingPermission },
|
|
758
|
+
...entry.backend !== void 0 && { backend: entry.backend },
|
|
759
|
+
...entry.nestedCount !== void 0 && { nestedCount: entry.nestedCount }
|
|
760
|
+
}));
|
|
761
|
+
return Object.freeze({
|
|
762
|
+
entries: Object.freeze(publicEntries),
|
|
763
|
+
timestamp,
|
|
764
|
+
generation
|
|
765
|
+
});
|
|
766
|
+
}
|
|
767
|
+
/** Copy and freeze a terminal payload before publishing it. */
|
|
768
|
+
function createRunTerminalPayload(payload) {
|
|
769
|
+
return Object.freeze({
|
|
770
|
+
runId: payload.runId,
|
|
771
|
+
supervisorKey: createSupervisorKey(payload.supervisorId),
|
|
772
|
+
...payload.runtimeId !== void 0 && { runtimeId: payload.runtimeId },
|
|
773
|
+
label: payload.label,
|
|
774
|
+
agent: payload.agent,
|
|
775
|
+
status: payload.status,
|
|
776
|
+
...payload.nestedCount !== void 0 && { nestedCount: payload.nestedCount },
|
|
777
|
+
...payload.backend !== void 0 && { backend: payload.backend }
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
/** Copy, bound, sanitize, and freeze a polling error before publishing it. */
|
|
781
|
+
function createPollErrorPayload(payload) {
|
|
782
|
+
return Object.freeze({
|
|
783
|
+
source: payload.source,
|
|
784
|
+
...payload.runId !== void 0 && { runId: payload.runId },
|
|
785
|
+
message: sanitizeAndBoundText(payload.message),
|
|
786
|
+
error: sanitizeAndBoundText(payload.error),
|
|
787
|
+
count: payload.count,
|
|
788
|
+
timestamp: payload.timestamp
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
//#endregion
|
|
792
|
+
//#region src/render.ts
|
|
793
|
+
const FINAL_OUTPUT_CHARS = 600;
|
|
794
|
+
const SCALAR_CHARS = 240;
|
|
795
|
+
const EVENT_CHARS = 600;
|
|
796
|
+
const PREVIEW_LINES = 12;
|
|
797
|
+
const STATUS_ROWS = 12;
|
|
798
|
+
const COLLECTION_ITEMS = 8;
|
|
799
|
+
const INSPECT_ITEMS = 5;
|
|
800
|
+
const CLEANUP_ITEMS = 8;
|
|
801
|
+
function asRecord(value) {
|
|
802
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
803
|
+
}
|
|
804
|
+
function stringValue(value) {
|
|
805
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
806
|
+
}
|
|
807
|
+
function numberValue(value) {
|
|
808
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
809
|
+
if (typeof value === "string" && value.trim() && Number.isFinite(Number(value))) return Number(value);
|
|
810
|
+
}
|
|
811
|
+
function itemMarker(count) {
|
|
812
|
+
return `… ${count} items omitted`;
|
|
813
|
+
}
|
|
814
|
+
function lineMarker(count) {
|
|
815
|
+
return `… ${count} lines omitted`;
|
|
816
|
+
}
|
|
817
|
+
function characterMarker(count) {
|
|
818
|
+
return `[preview clipped: ${count} characters omitted]`;
|
|
819
|
+
}
|
|
820
|
+
function cleanMultiline(value) {
|
|
821
|
+
if (typeof value !== "string") return void 0;
|
|
822
|
+
return sanitizeText(value).trim() || void 0;
|
|
823
|
+
}
|
|
824
|
+
function leadingClip(value, limit) {
|
|
825
|
+
if (value.length <= limit) return { text: value };
|
|
826
|
+
let prefixLength = Math.max(0, limit - characterMarker(value.length).length);
|
|
827
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
828
|
+
const omitted = value.length - prefixLength;
|
|
829
|
+
const nextPrefixLength = Math.max(0, limit - characterMarker(omitted).length);
|
|
830
|
+
if (nextPrefixLength === prefixLength) break;
|
|
831
|
+
prefixLength = nextPrefixLength;
|
|
832
|
+
}
|
|
833
|
+
const clipped = clipText(value, Math.max(1, prefixLength + 1)) ?? "";
|
|
834
|
+
const text = clipped.endsWith("…") ? clipped.slice(0, -1) : clipped.slice(0, prefixLength);
|
|
835
|
+
return {
|
|
836
|
+
text,
|
|
837
|
+
omitted: value.length - text.length
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
function scalar(value, fallback = "unknown", limit = SCALAR_CHARS) {
|
|
841
|
+
const clean = cleanMultiline(typeof value === "string" ? value : void 0);
|
|
842
|
+
if (!clean) return fallback;
|
|
843
|
+
const clipped = leadingClip(clean.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim(), limit);
|
|
844
|
+
return clipped.omitted === void 0 ? clipped.text : `${clipped.text}${characterMarker(clipped.omitted)}`;
|
|
845
|
+
}
|
|
846
|
+
function optionalScalar(value, limit = SCALAR_CHARS) {
|
|
847
|
+
const clean = cleanMultiline(typeof value === "string" ? value : void 0);
|
|
848
|
+
return clean ? scalar(clean, "unknown", limit) : void 0;
|
|
849
|
+
}
|
|
850
|
+
function preview(value, limit) {
|
|
851
|
+
const clean = cleanMultiline(value);
|
|
852
|
+
if (!clean) return void 0;
|
|
853
|
+
const clipped = leadingClip(clean, limit);
|
|
854
|
+
const lines = clipped.text.split("\n").slice(0, PREVIEW_LINES);
|
|
855
|
+
const omittedLines = Math.max(0, clean.split("\n").length - lines.length);
|
|
856
|
+
return [
|
|
857
|
+
...lines,
|
|
858
|
+
...clipped.omitted === void 0 ? [] : [characterMarker(clipped.omitted)],
|
|
859
|
+
...omittedLines === 0 ? [] : [lineMarker(omittedLines)]
|
|
860
|
+
];
|
|
861
|
+
}
|
|
862
|
+
function quoted(value) {
|
|
863
|
+
return `"${scalar(value).replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
|
|
864
|
+
}
|
|
865
|
+
function optionalQuoted(value) {
|
|
866
|
+
return stringValue(value) ? quoted(value) : void 0;
|
|
867
|
+
}
|
|
868
|
+
function optionalNumber(value) {
|
|
869
|
+
return numberValue(value) === void 0 ? void 0 : String(numberValue(value));
|
|
870
|
+
}
|
|
871
|
+
function selected(record, key, fallback, defaultValue = "unknown") {
|
|
872
|
+
return scalar(stringValue(record?.[key]) ?? stringValue(fallback), defaultValue);
|
|
873
|
+
}
|
|
874
|
+
function selectedOptional(record, key) {
|
|
875
|
+
return optionalScalar(record?.[key]);
|
|
876
|
+
}
|
|
877
|
+
function hasStatusIdentity(record) {
|
|
878
|
+
return !!record && !!(stringValue(record.run_id) || stringValue(record.label) || stringValue(record.status));
|
|
879
|
+
}
|
|
880
|
+
function linesText(lines, theme) {
|
|
881
|
+
return new Text(lines.map((line, index) => theme.fg(index === 0 ? "toolTitle" : "muted", index === 0 ? theme.bold(line) : line)).join("\n"), 0, 0);
|
|
882
|
+
}
|
|
883
|
+
function fallback(tool) {
|
|
884
|
+
return [`Avenor ${tool} result unavailable.`, "Retry the tool or use avenor_status/avenor_inspect."];
|
|
885
|
+
}
|
|
886
|
+
function partial(result) {
|
|
887
|
+
const details = asRecord(result.details);
|
|
888
|
+
const label = optionalScalar(details?.label);
|
|
889
|
+
const status = optionalScalar(details?.status);
|
|
890
|
+
if (label && status) return [`Working: ${label} — ${status}`];
|
|
891
|
+
if (status) return [`Working: ${status}`];
|
|
892
|
+
if (label) return [`Working: ${label}`];
|
|
893
|
+
const content = result.content?.[0]?.text;
|
|
894
|
+
const firstLine = typeof content === "string" ? content.split(/\r?\n/).find((line) => line.trim()) : void 0;
|
|
895
|
+
if (firstLine) {
|
|
896
|
+
const clean = scalar(firstLine, "", SCALAR_CHARS);
|
|
897
|
+
if (clean && !clean.startsWith("{") && !clean.startsWith("[")) return [`Working: ${clean}`];
|
|
898
|
+
}
|
|
899
|
+
return ["Working…"];
|
|
900
|
+
}
|
|
901
|
+
function isTerminal(status) {
|
|
902
|
+
return TERMINAL_STATUSES.has(status);
|
|
903
|
+
}
|
|
904
|
+
function permission(record) {
|
|
905
|
+
return asRecord(record?.pending_permission);
|
|
906
|
+
}
|
|
907
|
+
function permissionOptions(record) {
|
|
908
|
+
const options = record?.options;
|
|
909
|
+
return Array.isArray(options) ? options.map(asRecord).filter((value) => !!value) : [];
|
|
910
|
+
}
|
|
911
|
+
function optionRows(options) {
|
|
912
|
+
return options.slice(0, COLLECTION_ITEMS).map((option) => `${selected(option, "option_id")} (${selected(option, "label")})`).join(", ");
|
|
913
|
+
}
|
|
914
|
+
function optionLine(prefix, options) {
|
|
915
|
+
const rows = optionRows(options);
|
|
916
|
+
const omitted = options.length - Math.min(options.length, COLLECTION_ITEMS);
|
|
917
|
+
return `${prefix}${rows ? ` ${rows}` : ""}${omitted > 0 ? `${rows ? " " : " "}${itemMarker(omitted)}` : ""}`;
|
|
918
|
+
}
|
|
919
|
+
function guidance(status, pending, runId) {
|
|
920
|
+
if (status === "waiting") {
|
|
921
|
+
const options = permissionOptions(pending);
|
|
922
|
+
if (options.length > 0) return `Guidance: Call avenor_answer_permission with run_id "${runId}" and option_id "${selected(options[0], "option_id")}".`;
|
|
923
|
+
if (pending) return `Guidance: Permission input is required, but no options are available; call avenor_inspect with run_id "${runId}".`;
|
|
924
|
+
return `Guidance: Call avenor_follow_up or avenor_status with run_id "${runId}".`;
|
|
925
|
+
}
|
|
926
|
+
if (isTerminal(status)) return `Guidance: Call avenor_result or avenor_events with run_id "${runId}".`;
|
|
927
|
+
return `Guidance: Call avenor_result with run_id "${runId}".`;
|
|
928
|
+
}
|
|
929
|
+
function statusExpanded(status) {
|
|
930
|
+
return [
|
|
931
|
+
["Phase", "phase"],
|
|
932
|
+
["Phase label", "phase_label"],
|
|
933
|
+
["Session", "session_id"],
|
|
934
|
+
["Backend", "backend"],
|
|
935
|
+
["Agent", "agent"],
|
|
936
|
+
["Model", "model"],
|
|
937
|
+
["Latest sequence", "latest_seq"],
|
|
938
|
+
["Stop reason", "stop_reason"]
|
|
939
|
+
].flatMap(([label, key]) => {
|
|
940
|
+
const value = key === "latest_seq" ? optionalNumber(status[key]) : selectedOptional(status, key);
|
|
941
|
+
return value === void 0 ? [] : [`${label}: ${value}`];
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
function statusLines(details, args, expanded) {
|
|
945
|
+
if (Array.isArray(details)) {
|
|
946
|
+
const statuses = details.map(asRecord);
|
|
947
|
+
if (statuses.some((status) => !hasStatusIdentity(status))) return void 0;
|
|
948
|
+
const displayed = statuses.slice(0, STATUS_ROWS);
|
|
949
|
+
const lines = [
|
|
950
|
+
`Avenor status — ${statuses.length} runs`,
|
|
951
|
+
...displayed.map((status) => {
|
|
952
|
+
const runId = selected(status, "run_id", args.run_id);
|
|
953
|
+
return `${selected(status, "label", void 0, runId)} — ${selected(status, "status")} (run_id: ${runId})`;
|
|
954
|
+
}),
|
|
955
|
+
...statuses.length > displayed.length ? [itemMarker(statuses.length - displayed.length)] : []
|
|
956
|
+
];
|
|
957
|
+
if (!expanded) return lines;
|
|
958
|
+
for (const status of displayed) {
|
|
959
|
+
const runId = selected(status, "run_id", args.run_id);
|
|
960
|
+
lines.push(`Run: ${runId}`, `Label: ${selected(status, "label", void 0, runId)}`, `Status: ${selected(status, "status")}`, ...statusExpanded(status));
|
|
961
|
+
}
|
|
962
|
+
return lines;
|
|
963
|
+
}
|
|
964
|
+
const status = asRecord(details);
|
|
965
|
+
if (!hasStatusIdentity(status)) return void 0;
|
|
966
|
+
const runId = selected(status, "run_id", args.run_id);
|
|
967
|
+
const label = selected(status, "label", void 0, runId);
|
|
968
|
+
const state = selected(status, "status");
|
|
969
|
+
const pending = permission(status);
|
|
970
|
+
const options = permissionOptions(pending);
|
|
971
|
+
const finalPreview = preview(status.final_output, FINAL_OUTPUT_CHARS);
|
|
972
|
+
const lines = [
|
|
973
|
+
`Status: ${label} — ${state}`,
|
|
974
|
+
`Run: ${runId}`,
|
|
975
|
+
...pending ? [`Permission: ${selected(pending, "description")}`, optionLine("Options:", options)] : [],
|
|
976
|
+
...status.final_output_truncated === true ? [`Warning: final output preview is truncated; call avenor_result with run_id "${runId}".`] : [],
|
|
977
|
+
...finalPreview ? ["Final output:", ...finalPreview] : ["Final output: unavailable."],
|
|
978
|
+
guidance(state, pending, runId)
|
|
979
|
+
];
|
|
980
|
+
return expanded ? [...lines, ...statusExpanded(status)] : lines;
|
|
981
|
+
}
|
|
982
|
+
function resultLines(details, args, expanded) {
|
|
983
|
+
const result = asRecord(details);
|
|
984
|
+
if (!hasStatusIdentity(result)) return void 0;
|
|
985
|
+
const runId = selected(result, "run_id", args.run_id);
|
|
986
|
+
const label = selected(result, "label", void 0, runId);
|
|
987
|
+
const state = selected(result, "status");
|
|
988
|
+
const pending = permission(result);
|
|
989
|
+
const finalPreview = preview(result.output, FINAL_OUTPUT_CHARS);
|
|
990
|
+
const lines = [
|
|
991
|
+
`Result: ${label} — ${state}`,
|
|
992
|
+
`Run: ${runId}`,
|
|
993
|
+
...result.timed_out === true ? ["Warning: wait timed out; the run is still active."] : [],
|
|
994
|
+
...pending ? [`Permission: ${selected(pending, "description")}`] : [],
|
|
995
|
+
guidance(state, pending, runId),
|
|
996
|
+
...result.output_truncated === true ? [stringValue(result.output_event_path) ? `Warning: final output may be truncated; retry avenor_result or read the durable event path "${scalar(result.output_event_path)}".` : `Warning: final output may be truncated; call avenor_events with run_id "${runId}".`] : [],
|
|
997
|
+
...finalPreview ? ["Final output:", ...finalPreview] : ["Final output: unavailable."]
|
|
998
|
+
];
|
|
999
|
+
if (!expanded) return lines;
|
|
1000
|
+
const session = selectedOptional(result, "session_id");
|
|
1001
|
+
const stopReason = selectedOptional(result, "stop_reason");
|
|
1002
|
+
return [
|
|
1003
|
+
...lines,
|
|
1004
|
+
...session ? [`Session: ${session}`] : [],
|
|
1005
|
+
...stopReason ? [`Stop reason: ${stopReason}`] : []
|
|
1006
|
+
];
|
|
1007
|
+
}
|
|
1008
|
+
function permissionLines(details, args) {
|
|
1009
|
+
const result = asRecord(details);
|
|
1010
|
+
if (!result || typeof result.ok !== "boolean") return void 0;
|
|
1011
|
+
const runId = scalar(args.run_id);
|
|
1012
|
+
return [
|
|
1013
|
+
`Permission answer: ${result.ok ? "accepted" : "failed"}.`,
|
|
1014
|
+
`Run: ${runId}`,
|
|
1015
|
+
`Option: ${scalar(args.option_id)}`,
|
|
1016
|
+
...stringValue(args.request_id) ? [`Request: ${scalar(args.request_id)}`] : [],
|
|
1017
|
+
`Guidance: Call avenor_status with run_id "${runId}" to verify the permission state.`
|
|
1018
|
+
];
|
|
1019
|
+
}
|
|
1020
|
+
function followUpLines(details, args) {
|
|
1021
|
+
const result = asRecord(details);
|
|
1022
|
+
if (!result || !stringValue(result.run_id) || !stringValue(result.label)) return void 0;
|
|
1023
|
+
const runId = selected(result, "run_id", args.run_id);
|
|
1024
|
+
const label = selected(result, "label", args.label, runId);
|
|
1025
|
+
return [
|
|
1026
|
+
`Follow-up: ${label} — dispatched.`,
|
|
1027
|
+
`Source run: ${scalar(args.run_id)}`,
|
|
1028
|
+
`Run: ${runId}`,
|
|
1029
|
+
`Label: ${label}`,
|
|
1030
|
+
`Guidance: Call avenor_status with run_id "${runId}".`
|
|
1031
|
+
];
|
|
1032
|
+
}
|
|
1033
|
+
function eventSequence(value) {
|
|
1034
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
1035
|
+
return typeof value === "string" && value.trim() && Number.isFinite(Number(value)) ? value : void 0;
|
|
1036
|
+
}
|
|
1037
|
+
function eventRow(value) {
|
|
1038
|
+
const name = stringValue(value.event) ?? stringValue(value.type) ?? "unknown";
|
|
1039
|
+
const sequence = eventSequence(value.seq);
|
|
1040
|
+
const text = extractEventText(value);
|
|
1041
|
+
const previewText = text ? scalar(text, "unknown", EVENT_CHARS) : void 0;
|
|
1042
|
+
return `Event ${sequence ?? "?"}: ${scalar(name)}${previewText ? ` — ${previewText}` : ""}`;
|
|
1043
|
+
}
|
|
1044
|
+
function eventsLines(details, args, expanded) {
|
|
1045
|
+
const result = asRecord(details);
|
|
1046
|
+
if (!result || !Array.isArray(result.events) || result.events.some((event) => !asRecord(event))) return void 0;
|
|
1047
|
+
const events = result.events.map((event) => event);
|
|
1048
|
+
const runId = scalar(args.run_id);
|
|
1049
|
+
const types = Array.isArray(args.types) ? args.types : [];
|
|
1050
|
+
const displayedTypes = types.slice(0, COLLECTION_ITEMS).map((type) => scalar(type)).join(", ");
|
|
1051
|
+
const lines = [`Events: ${runId} — ${events.length} events`, ...types.length > 0 ? [`Filter: ${displayedTypes}${types.length > COLLECTION_ITEMS ? ` ${itemMarker(types.length - COLLECTION_ITEMS)}` : ""}`] : []];
|
|
1052
|
+
if (events.length === 0) return [...lines, "No events."];
|
|
1053
|
+
if (!expanded) {
|
|
1054
|
+
lines.push(`Preview: ${eventRow(events[0])}`);
|
|
1055
|
+
if (events.length > 1) lines.push(itemMarker(events.length - 1));
|
|
1056
|
+
return lines;
|
|
1057
|
+
}
|
|
1058
|
+
const displayed = events.slice(0, STATUS_ROWS);
|
|
1059
|
+
lines.push(...displayed.map(eventRow));
|
|
1060
|
+
if (events.length > displayed.length) lines.push(itemMarker(events.length - displayed.length));
|
|
1061
|
+
return lines;
|
|
1062
|
+
}
|
|
1063
|
+
function inspectTranscriptRow(value) {
|
|
1064
|
+
const kind = stringValue(value.kind);
|
|
1065
|
+
const text = optionalScalar(value.text);
|
|
1066
|
+
if (!kind) return void 0;
|
|
1067
|
+
if (kind === "tool") {
|
|
1068
|
+
const title = selected(value, "title", stringValue(value.kind) ?? stringValue(value.key));
|
|
1069
|
+
const status = optionalScalar(value.status);
|
|
1070
|
+
return `${title}${status ? ` (${status})` : ""}${text ? `: ${text}` : ""}`;
|
|
1071
|
+
}
|
|
1072
|
+
if (!text) return void 0;
|
|
1073
|
+
if ([
|
|
1074
|
+
"assistant",
|
|
1075
|
+
"thought",
|
|
1076
|
+
"user",
|
|
1077
|
+
"permission",
|
|
1078
|
+
"status",
|
|
1079
|
+
"session"
|
|
1080
|
+
].includes(kind)) return `${kind}: ${text}`;
|
|
1081
|
+
}
|
|
1082
|
+
function inspectToolRow(prefix, value) {
|
|
1083
|
+
const title = selected(value, "title", stringValue(value.kind) ?? stringValue(value.key));
|
|
1084
|
+
const status = optionalScalar(value.status);
|
|
1085
|
+
const text = optionalScalar(value.preview);
|
|
1086
|
+
return `${prefix}: ${title}${status ? ` (${status})` : ""}${text ? `: ${text}` : ""}`;
|
|
1087
|
+
}
|
|
1088
|
+
function inspectPermissionRow(value) {
|
|
1089
|
+
const description = scalar(stringValue(value.description) ?? stringValue(value.question) ?? stringValue(value.tool) ?? stringValue(value.kind));
|
|
1090
|
+
const options = permissionOptions(value);
|
|
1091
|
+
const rows = optionRows(options);
|
|
1092
|
+
return `Permission: ${description} (request_id: ${selected(value, "request_id")}); options:${rows ? ` ${rows}` : ""}${options.length > COLLECTION_ITEMS ? ` ${itemMarker(options.length - COLLECTION_ITEMS)}` : ""}`;
|
|
1093
|
+
}
|
|
1094
|
+
function inspectGroup(title, values, row) {
|
|
1095
|
+
const rows = (Array.isArray(values) ? values.map(asRecord).filter((value) => !!value) : []).map(row).filter((value) => !!value);
|
|
1096
|
+
return [
|
|
1097
|
+
`${title}:`,
|
|
1098
|
+
...rows.slice(0, INSPECT_ITEMS),
|
|
1099
|
+
...rows.length > INSPECT_ITEMS ? [itemMarker(rows.length - INSPECT_ITEMS)] : []
|
|
1100
|
+
];
|
|
1101
|
+
}
|
|
1102
|
+
function inspectLines(details, args, expanded) {
|
|
1103
|
+
const inspect = asRecord(details);
|
|
1104
|
+
const status = asRecord(inspect?.status);
|
|
1105
|
+
if (!inspect || !status || !hasStatusIdentity(inspect) && !hasStatusIdentity(status)) return void 0;
|
|
1106
|
+
const runId = selected(inspect, "run_id", args.run_id);
|
|
1107
|
+
const label = selected(inspect, "label", void 0, runId);
|
|
1108
|
+
const state = selected(status, "status");
|
|
1109
|
+
const finalPreview = preview(inspect.final_output, FINAL_OUTPUT_CHARS);
|
|
1110
|
+
const transcript = Array.isArray(inspect.transcript) ? inspect.transcript : [];
|
|
1111
|
+
const tools = Array.isArray(inspect.tools) ? inspect.tools : [];
|
|
1112
|
+
const liveTools = Array.isArray(inspect.live_tools) ? inspect.live_tools : [];
|
|
1113
|
+
const permissions = Array.isArray(inspect.permissions) ? inspect.permissions : [];
|
|
1114
|
+
const lines = [
|
|
1115
|
+
`Inspect: ${label} — ${state}`,
|
|
1116
|
+
`Run: ${runId}`,
|
|
1117
|
+
`Counts: transcript=${transcript.length}, tools=${tools.length}, live_tools=${liveTools.length}, permissions=${permissions.length}`,
|
|
1118
|
+
...finalPreview ? ["Final output:", ...finalPreview] : ["Final output: unavailable."],
|
|
1119
|
+
`Guidance: Call avenor_events with run_id "${runId}" for raw events.`
|
|
1120
|
+
];
|
|
1121
|
+
if (!expanded) return lines;
|
|
1122
|
+
return [
|
|
1123
|
+
...lines,
|
|
1124
|
+
...inspectGroup("Transcript", transcript, inspectTranscriptRow),
|
|
1125
|
+
...inspectGroup("Tools", tools, (value) => inspectToolRow("Tool", value)),
|
|
1126
|
+
...inspectGroup("Live tools", liveTools, (value) => inspectToolRow("Live tool", value)),
|
|
1127
|
+
...inspectGroup("Permissions", permissions, inspectPermissionRow)
|
|
1128
|
+
];
|
|
1129
|
+
}
|
|
1130
|
+
function shutdownLines(details, args, expanded) {
|
|
1131
|
+
const result = asRecord(details);
|
|
1132
|
+
if (!result || typeof result.ok !== "boolean" || !Array.isArray(result.cleaned_up)) return void 0;
|
|
1133
|
+
const paths = result.cleaned_up.filter((path) => typeof path === "string");
|
|
1134
|
+
const supervisor = stringValue(args.supervisor_id) ? scalar(args.supervisor_id) : "singleton";
|
|
1135
|
+
const lines = [
|
|
1136
|
+
`Avenor shutdown: ${result.ok ? "succeeded" : "failed"}.`,
|
|
1137
|
+
`Supervisor: ${supervisor}`,
|
|
1138
|
+
`Mode: ${args.force === true ? "force" : "graceful"}`,
|
|
1139
|
+
paths.length > 0 ? `Cleanup: ${paths.length} paths` : "Cleanup: none",
|
|
1140
|
+
...!result.ok ? ["Warning: shutdown did not complete.", "Guidance: retry avenor_shutdown."] : []
|
|
1141
|
+
];
|
|
1142
|
+
if (!expanded) return lines;
|
|
1143
|
+
return [
|
|
1144
|
+
...lines,
|
|
1145
|
+
...paths.slice(0, CLEANUP_ITEMS).map((path) => `Cleanup path: ${scalar(path)}`),
|
|
1146
|
+
...paths.length > CLEANUP_ITEMS ? [itemMarker(paths.length - CLEANUP_ITEMS)] : []
|
|
1147
|
+
];
|
|
1148
|
+
}
|
|
1149
|
+
function render(tool, result, options, theme, build) {
|
|
1150
|
+
try {
|
|
1151
|
+
return linesText(options.isPartial ? partial(result) : build() ?? fallback(tool), theme);
|
|
1152
|
+
} catch {
|
|
1153
|
+
return new Text(fallback(tool).join("\n"), 0, 0);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
function renderStatusCall(args, theme) {
|
|
1157
|
+
const pieces = stringValue(args.run_id) ? [`avenor_status run_id ${quoted(args.run_id)}`] : ["avenor_status (all runs)"];
|
|
1158
|
+
const view = optionalQuoted(args.view);
|
|
1159
|
+
const supervisor = optionalQuoted(args.supervisor_id);
|
|
1160
|
+
if (view) pieces.push(`view ${view}`);
|
|
1161
|
+
if (supervisor) pieces.push(`supervisor_id ${supervisor}`);
|
|
1162
|
+
return linesText([pieces.join(" ")], theme);
|
|
1163
|
+
}
|
|
1164
|
+
function renderResultCall(args, theme) {
|
|
1165
|
+
const pieces = [`avenor_result run_id ${quoted(args.run_id)}`];
|
|
1166
|
+
if (args.wait !== void 0) pieces.push(`wait=${args.wait}`);
|
|
1167
|
+
const timeout = optionalQuoted(args.timeout);
|
|
1168
|
+
const supervisor = optionalQuoted(args.supervisor_id);
|
|
1169
|
+
if (timeout) pieces.push(`timeout ${timeout}`);
|
|
1170
|
+
if (supervisor) pieces.push(`supervisor_id ${supervisor}`);
|
|
1171
|
+
return linesText([pieces.join(" ")], theme);
|
|
1172
|
+
}
|
|
1173
|
+
function renderInspectCall(args, theme) {
|
|
1174
|
+
const pieces = [`avenor_inspect run_id ${quoted(args.run_id)}`];
|
|
1175
|
+
const limit = optionalNumber(args.limit);
|
|
1176
|
+
const afterSequence = optionalNumber(args.after_seq);
|
|
1177
|
+
const supervisor = optionalQuoted(args.supervisor_id);
|
|
1178
|
+
if (limit) pieces.push(`limit=${limit}`);
|
|
1179
|
+
if (afterSequence) pieces.push(`after_seq=${afterSequence}`);
|
|
1180
|
+
if (supervisor) pieces.push(`supervisor_id ${supervisor}`);
|
|
1181
|
+
return linesText([pieces.join(" ")], theme);
|
|
1182
|
+
}
|
|
1183
|
+
function renderAnswerPermissionCall(args, theme) {
|
|
1184
|
+
const pieces = [`avenor_answer_permission run_id ${quoted(args.run_id)}`, `option_id ${quoted(args.option_id)}`];
|
|
1185
|
+
const request = optionalQuoted(args.request_id);
|
|
1186
|
+
const message = optionalQuoted(args.message);
|
|
1187
|
+
const supervisor = optionalQuoted(args.supervisor_id);
|
|
1188
|
+
if (request) pieces.push(`request_id ${request}`);
|
|
1189
|
+
if (message) pieces.push(`message ${message}`);
|
|
1190
|
+
if (supervisor) pieces.push(`supervisor_id ${supervisor}`);
|
|
1191
|
+
return linesText([pieces.join(" ")], theme);
|
|
1192
|
+
}
|
|
1193
|
+
function renderFollowUpCall(args, theme) {
|
|
1194
|
+
const pieces = [`avenor_follow_up run_id ${quoted(args.run_id)}`, `message ${quoted(args.message)}`];
|
|
1195
|
+
const label = optionalQuoted(args.label);
|
|
1196
|
+
const supervisor = optionalQuoted(args.supervisor_id);
|
|
1197
|
+
if (label) pieces.push(`label ${label}`);
|
|
1198
|
+
if (supervisor) pieces.push(`supervisor_id ${supervisor}`);
|
|
1199
|
+
return linesText([pieces.join(" ")], theme);
|
|
1200
|
+
}
|
|
1201
|
+
function renderEventsCall(args, theme) {
|
|
1202
|
+
const pieces = [`avenor_events run_id ${quoted(args.run_id)}`];
|
|
1203
|
+
if (Array.isArray(args.types)) {
|
|
1204
|
+
const shown = args.types.slice(0, COLLECTION_ITEMS).map(quoted).join(", ");
|
|
1205
|
+
pieces.push(`types=[${shown}]${args.types.length > COLLECTION_ITEMS ? ` ${itemMarker(args.types.length - COLLECTION_ITEMS)}` : ""}`);
|
|
1206
|
+
}
|
|
1207
|
+
const limit = optionalNumber(args.limit);
|
|
1208
|
+
const supervisor = optionalQuoted(args.supervisor_id);
|
|
1209
|
+
if (limit) pieces.push(`limit=${limit}`);
|
|
1210
|
+
if (supervisor) pieces.push(`supervisor_id ${supervisor}`);
|
|
1211
|
+
return linesText([pieces.join(" ")], theme);
|
|
1212
|
+
}
|
|
1213
|
+
function renderShutdownCall(args, theme) {
|
|
1214
|
+
const pieces = ["avenor_shutdown"];
|
|
1215
|
+
const supervisor = optionalQuoted(args.supervisor_id);
|
|
1216
|
+
if (supervisor) pieces.push(`supervisor_id ${supervisor}`);
|
|
1217
|
+
if (args.force !== void 0) pieces.push(`force=${args.force}`);
|
|
1218
|
+
return linesText([pieces.join(" ")], theme);
|
|
1219
|
+
}
|
|
1220
|
+
function renderStatusResult(result, options, theme, args) {
|
|
1221
|
+
return render("status", result, options, theme, () => statusLines(result.details, args, options.expanded));
|
|
1222
|
+
}
|
|
1223
|
+
function renderResultResult(result, options, theme, args) {
|
|
1224
|
+
return render("result", result, options, theme, () => resultLines(result.details, args, options.expanded));
|
|
1225
|
+
}
|
|
1226
|
+
function renderInspectResult(result, options, theme, args) {
|
|
1227
|
+
return render("inspect", result, options, theme, () => inspectLines(result.details, args, options.expanded));
|
|
1228
|
+
}
|
|
1229
|
+
function renderAnswerPermissionResult(result, options, theme, args) {
|
|
1230
|
+
return render("answer_permission", result, options, theme, () => permissionLines(result.details, args));
|
|
1231
|
+
}
|
|
1232
|
+
function renderFollowUpResult(result, options, theme, args) {
|
|
1233
|
+
return render("follow_up", result, options, theme, () => followUpLines(result.details, args));
|
|
1234
|
+
}
|
|
1235
|
+
function renderEventsResult(result, options, theme, args) {
|
|
1236
|
+
return render("events", result, options, theme, () => eventsLines(result.details, args, options.expanded));
|
|
1237
|
+
}
|
|
1238
|
+
function renderShutdownResult(result, options, theme, args) {
|
|
1239
|
+
return render("shutdown", result, options, theme, () => shutdownLines(result.details, args, options.expanded));
|
|
1240
|
+
}
|
|
1241
|
+
//#endregion
|
|
705
1242
|
//#region src/index.ts
|
|
706
1243
|
const POLL_INTERVAL_MS = 3e3;
|
|
707
1244
|
const INSPECT_LIMIT = 128;
|
|
@@ -710,6 +1247,7 @@ const INSPECT_JSON_STRING_CHARS = 600;
|
|
|
710
1247
|
const INSPECT_JSON_ARRAY_ITEMS = 24;
|
|
711
1248
|
const INSPECT_JSON_OBJECT_KEYS = 24;
|
|
712
1249
|
const INSPECT_JSON_DEPTH = 8;
|
|
1250
|
+
const MAX_POLL_ERRORS = 100;
|
|
713
1251
|
const defaultDeps = {
|
|
714
1252
|
spawnTool,
|
|
715
1253
|
statusTool,
|
|
@@ -726,6 +1264,19 @@ const defaultDeps = {
|
|
|
726
1264
|
function statusSupervisorId(requestedSupervisorId, spawnedSupervisorId) {
|
|
727
1265
|
return requestedSupervisorId ? spawnedSupervisorId : void 0;
|
|
728
1266
|
}
|
|
1267
|
+
/** Project shared spawn identity into Pi tool metadata without resolving rosters here. */
|
|
1268
|
+
function spawnIdentityMetadata(params, status) {
|
|
1269
|
+
const metadata = {};
|
|
1270
|
+
const add = (key, value) => {
|
|
1271
|
+
if (typeof value === "string" && value.length > 0) metadata[key] = value;
|
|
1272
|
+
};
|
|
1273
|
+
add("roster_file", status?.roster_file ?? params.roster_file);
|
|
1274
|
+
add("roster_entry", status?.roster_entry ?? params.roster_entry);
|
|
1275
|
+
add("effective_agent", status?.effective_agent ?? status?.agent ?? params.agent);
|
|
1276
|
+
add("effective_model", status?.effective_model ?? status?.model ?? params.model);
|
|
1277
|
+
add("effective_backend", status?.effective_backend ?? status?.backend ?? params.backend);
|
|
1278
|
+
return metadata;
|
|
1279
|
+
}
|
|
729
1280
|
function sleep(ms) {
|
|
730
1281
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
731
1282
|
}
|
|
@@ -751,7 +1302,8 @@ function buildCompletionText(run, result, finalOutput) {
|
|
|
751
1302
|
}
|
|
752
1303
|
function buildWaitingText(run, status) {
|
|
753
1304
|
const perm = status.pending_permission;
|
|
754
|
-
const
|
|
1305
|
+
const agent = run.agent ?? status.effective_agent ?? status.agent ?? "roster";
|
|
1306
|
+
const lines = [`Sub-agent "${run.label}" (${agent}) is waiting for input.`];
|
|
755
1307
|
if (perm) lines.push(`Permission request: ${perm.description}`, `To answer: call \`avenor_answer_permission\` with run_id "${run.runId}", option_id "allow_once" | "allow_always" | "deny"`);
|
|
756
1308
|
else if (status.phase_label) lines.push(`Question: ${status.phase_label}`);
|
|
757
1309
|
if (!perm) lines.push(`Use \`avenor_follow_up\` with run_id "${run.runId}" to respond, or \`avenor_inspect\` to review context first.`);
|
|
@@ -837,11 +1389,34 @@ function toWatchRunRef(run) {
|
|
|
837
1389
|
}
|
|
838
1390
|
function createExtension(deps = defaultDeps) {
|
|
839
1391
|
return async function(pi) {
|
|
1392
|
+
let telemetryGeneration = 0;
|
|
840
1393
|
const trackedRuns = /* @__PURE__ */ new Map();
|
|
841
1394
|
let pollingActive = false;
|
|
842
1395
|
let pollingGeneration = 0;
|
|
843
1396
|
let pollInFlight = null;
|
|
844
1397
|
let sessionCtx = null;
|
|
1398
|
+
let pollingErrorCount = 0;
|
|
1399
|
+
const pollingErrors = [];
|
|
1400
|
+
let lastStatusEntries = [];
|
|
1401
|
+
function recordPollingError(source, message, error, runId) {
|
|
1402
|
+
pollingErrorCount++;
|
|
1403
|
+
const payload = createPollErrorPayload({
|
|
1404
|
+
source,
|
|
1405
|
+
runId,
|
|
1406
|
+
message,
|
|
1407
|
+
error,
|
|
1408
|
+
count: pollingErrorCount,
|
|
1409
|
+
timestamp: Date.now()
|
|
1410
|
+
});
|
|
1411
|
+
pollingErrors.push(payload);
|
|
1412
|
+
if (pollingErrors.length > MAX_POLL_ERRORS) pollingErrors.shift();
|
|
1413
|
+
emitAvenorEvent(pi.events, CHANNEL_POLL_ERROR, payload);
|
|
1414
|
+
}
|
|
1415
|
+
function clearPollingErrors() {
|
|
1416
|
+
pollingErrorCount = 0;
|
|
1417
|
+
pollingErrors.length = 0;
|
|
1418
|
+
updateWidget(lastStatusEntries);
|
|
1419
|
+
}
|
|
845
1420
|
function upsertTrackedRun(run, status, blocking = false) {
|
|
846
1421
|
const current = trackedRuns.get(run.runId);
|
|
847
1422
|
trackedRuns.set(run.runId, {
|
|
@@ -872,13 +1447,14 @@ function createExtension(deps = defaultDeps) {
|
|
|
872
1447
|
}
|
|
873
1448
|
}
|
|
874
1449
|
async function pollRuns() {
|
|
1450
|
+
const errorCountAtPollStart = pollingErrorCount;
|
|
875
1451
|
const liveMap = /* @__PURE__ */ new Map();
|
|
876
1452
|
try {
|
|
877
1453
|
const allLive = await deps.statusTool({});
|
|
878
1454
|
const liveList = Array.isArray(allLive) ? allLive : [allLive];
|
|
879
1455
|
for (const result of liveList) liveMap.set(result.run_id, result);
|
|
880
1456
|
} catch (err) {
|
|
881
|
-
|
|
1457
|
+
recordPollingError("singleton-list", "failed to list singleton runs", err);
|
|
882
1458
|
}
|
|
883
1459
|
const entries = [];
|
|
884
1460
|
for (const run of trackedRuns.values()) {
|
|
@@ -886,13 +1462,16 @@ function createExtension(deps = defaultDeps) {
|
|
|
886
1462
|
if (live) {
|
|
887
1463
|
liveMap.delete(live.run_id);
|
|
888
1464
|
run.lastStatus = live;
|
|
1465
|
+
run.agent = live.agent ?? run.agent;
|
|
889
1466
|
entries.push({
|
|
890
1467
|
runId: run.runId,
|
|
1468
|
+
supervisorId: run.supervisorId,
|
|
1469
|
+
runtimeId: live.runtime_id ?? run.runtimeId,
|
|
891
1470
|
label: run.label,
|
|
892
1471
|
status: live.status,
|
|
893
1472
|
phase: live.phase,
|
|
894
1473
|
phaseLabel: live.phase_label,
|
|
895
|
-
agent: run.agent,
|
|
1474
|
+
agent: run.agent ?? "unknown",
|
|
896
1475
|
pendingPermission: !!live.pending_permission,
|
|
897
1476
|
permissionDescription: live.pending_permission?.description,
|
|
898
1477
|
pid: live.pid,
|
|
@@ -908,28 +1487,33 @@ function createExtension(deps = defaultDeps) {
|
|
|
908
1487
|
const result = Array.isArray(raw) ? raw[0] : raw;
|
|
909
1488
|
if (!result) throw new Error("status response was empty");
|
|
910
1489
|
run.lastStatus = result;
|
|
1490
|
+
run.agent = result.agent ?? run.agent;
|
|
911
1491
|
entries.push({
|
|
912
1492
|
runId: run.runId,
|
|
1493
|
+
supervisorId: run.supervisorId,
|
|
1494
|
+
runtimeId: result.runtime_id ?? run.runtimeId,
|
|
913
1495
|
label: run.label,
|
|
914
1496
|
status: result.status,
|
|
915
1497
|
phase: result.phase,
|
|
916
1498
|
phaseLabel: result.phase_label,
|
|
917
|
-
agent: run.agent,
|
|
1499
|
+
agent: run.agent ?? "unknown",
|
|
918
1500
|
pendingPermission: !!result.pending_permission,
|
|
919
1501
|
permissionDescription: result.pending_permission?.description,
|
|
920
1502
|
pid: result.pid,
|
|
921
1503
|
backend: result.backend
|
|
922
1504
|
});
|
|
923
1505
|
} catch (err) {
|
|
924
|
-
|
|
1506
|
+
recordPollingError("run-status", "statusTool failed while polling a run", err, run.runId);
|
|
925
1507
|
const previous = run.lastStatus;
|
|
926
1508
|
entries.push({
|
|
927
1509
|
runId: run.runId,
|
|
1510
|
+
supervisorId: run.supervisorId,
|
|
1511
|
+
runtimeId: previous?.runtime_id ?? run.runtimeId,
|
|
928
1512
|
label: run.label,
|
|
929
1513
|
status: previous?.status ?? "unavailable",
|
|
930
1514
|
phase: previous?.phase,
|
|
931
1515
|
phaseLabel: previous?.phase_label,
|
|
932
|
-
agent: run.agent,
|
|
1516
|
+
agent: run.agent ?? previous?.agent ?? "unknown",
|
|
933
1517
|
pendingPermission: !!previous?.pending_permission,
|
|
934
1518
|
permissionDescription: previous?.pending_permission?.description,
|
|
935
1519
|
pid: previous?.pid,
|
|
@@ -939,6 +1523,7 @@ function createExtension(deps = defaultDeps) {
|
|
|
939
1523
|
}
|
|
940
1524
|
for (const live of liveMap.values()) entries.push({
|
|
941
1525
|
runId: live.run_id,
|
|
1526
|
+
runtimeId: live.runtime_id,
|
|
942
1527
|
label: live.label,
|
|
943
1528
|
status: live.status,
|
|
944
1529
|
phase: live.phase,
|
|
@@ -955,21 +1540,24 @@ function createExtension(deps = defaultDeps) {
|
|
|
955
1540
|
let idx = 0;
|
|
956
1541
|
for (const entry of entries) if (entry.backend === "pi" && entry.pid && !isTerminalStatus(entry.status)) entry.nestedCount = nestedCounts[idx++] ?? 0;
|
|
957
1542
|
}
|
|
1543
|
+
if (pollingErrorCount === errorCountAtPollStart) pollingErrorCount = 0;
|
|
958
1544
|
return entries;
|
|
959
1545
|
}
|
|
960
1546
|
function updateWidget(entries) {
|
|
1547
|
+
lastStatusEntries = entries;
|
|
961
1548
|
if (!sessionCtx) return;
|
|
962
1549
|
const lines = renderStatusLines(entries);
|
|
1550
|
+
const errorSuffix = pollingErrorCount > 0 ? `errors:${pollingErrorCount}` : "";
|
|
963
1551
|
if (lines.length > 0) {
|
|
964
1552
|
sessionCtx.ui.setWidget("avenor-status", lines);
|
|
965
1553
|
const statusStr = lines.map((line) => line.slice(0, 60)).join(" ");
|
|
966
1554
|
const nestedTotal = entries.filter((e) => !isTerminalStatus(e.status)).reduce((sum, e) => sum + (e.nestedCount ?? 0), 0);
|
|
967
1555
|
const total = lines.length + nestedTotal;
|
|
968
|
-
const statusWithTotal = nestedTotal > 0 ? `${statusStr} total:${total}` : statusStr;
|
|
1556
|
+
const statusWithTotal = [nestedTotal > 0 ? `${statusStr} total:${total}` : statusStr, errorSuffix].filter(Boolean).join(" ");
|
|
969
1557
|
sessionCtx.ui.setStatus("avenor", statusWithTotal);
|
|
970
1558
|
} else {
|
|
971
1559
|
sessionCtx.ui.setWidget("avenor-status", void 0);
|
|
972
|
-
sessionCtx.ui.setStatus("avenor",
|
|
1560
|
+
sessionCtx.ui.setStatus("avenor", errorSuffix);
|
|
973
1561
|
}
|
|
974
1562
|
}
|
|
975
1563
|
function startPolling() {
|
|
@@ -987,6 +1575,7 @@ function createExtension(deps = defaultDeps) {
|
|
|
987
1575
|
});
|
|
988
1576
|
if (!pollingActive || generation !== pollingGeneration) return;
|
|
989
1577
|
updateWidget(entries);
|
|
1578
|
+
emitAvenorEvent(pi.events, CHANNEL_POLL_COMPLETED, createPollCompletedPayload(entries, Date.now(), ++telemetryGeneration));
|
|
990
1579
|
for (const entry of entries) {
|
|
991
1580
|
const run = trackedRuns.get(entry.runId);
|
|
992
1581
|
if (!run) continue;
|
|
@@ -1009,6 +1598,16 @@ function createExtension(deps = defaultDeps) {
|
|
|
1009
1598
|
} catch (err) {
|
|
1010
1599
|
console.error("avenor tick: sendUserMessage failed", err);
|
|
1011
1600
|
}
|
|
1601
|
+
emitAvenorEvent(pi.events, CHANNEL_RUN_TERMINAL, createRunTerminalPayload({
|
|
1602
|
+
runId: entry.runId,
|
|
1603
|
+
supervisorId: entry.supervisorId,
|
|
1604
|
+
runtimeId: entry.runtimeId,
|
|
1605
|
+
label: entry.label,
|
|
1606
|
+
agent: entry.agent,
|
|
1607
|
+
status: entry.status,
|
|
1608
|
+
nestedCount: entry.nestedCount,
|
|
1609
|
+
backend: entry.backend
|
|
1610
|
+
}));
|
|
1012
1611
|
trackedRuns.delete(entry.runId);
|
|
1013
1612
|
} else if (entry.pendingPermission && !run.blocking && !run.permissionNotified) {
|
|
1014
1613
|
run.permissionNotified = true;
|
|
@@ -1031,8 +1630,7 @@ function createExtension(deps = defaultDeps) {
|
|
|
1031
1630
|
}, POLL_INTERVAL_MS);
|
|
1032
1631
|
else {
|
|
1033
1632
|
pollingActive = false;
|
|
1034
|
-
|
|
1035
|
-
sessionCtx?.ui.setWidget("avenor-status", void 0);
|
|
1633
|
+
updateWidget([]);
|
|
1036
1634
|
}
|
|
1037
1635
|
};
|
|
1038
1636
|
tick().catch((err) => {
|
|
@@ -1044,8 +1642,10 @@ function createExtension(deps = defaultDeps) {
|
|
|
1044
1642
|
pollingActive = false;
|
|
1045
1643
|
pollingGeneration++;
|
|
1046
1644
|
await pollInFlight?.catch(() => {});
|
|
1047
|
-
|
|
1048
|
-
|
|
1645
|
+
pollingErrorCount = 0;
|
|
1646
|
+
pollingErrors.length = 0;
|
|
1647
|
+
lastStatusEntries = [];
|
|
1648
|
+
updateWidget([]);
|
|
1049
1649
|
}
|
|
1050
1650
|
function createWatchDeps() {
|
|
1051
1651
|
async function resolveSupervisorSocket(run) {
|
|
@@ -1230,7 +1830,7 @@ function createExtension(deps = defaultDeps) {
|
|
|
1230
1830
|
aborted: false
|
|
1231
1831
|
};
|
|
1232
1832
|
} catch (err) {
|
|
1233
|
-
|
|
1833
|
+
recordPollingError("spawn-status", "statusTool failed while waiting for a run", err, run.runId);
|
|
1234
1834
|
}
|
|
1235
1835
|
}
|
|
1236
1836
|
return { aborted: true };
|
|
@@ -1242,6 +1842,9 @@ function createExtension(deps = defaultDeps) {
|
|
|
1242
1842
|
}
|
|
1243
1843
|
pi.on("session_start", async (_event, ctx) => {
|
|
1244
1844
|
sessionCtx = ctx;
|
|
1845
|
+
lastStatusEntries = [];
|
|
1846
|
+
pollingErrorCount = 0;
|
|
1847
|
+
pollingErrors.length = 0;
|
|
1245
1848
|
});
|
|
1246
1849
|
pi.on("session_shutdown", async () => {
|
|
1247
1850
|
await stopPolling();
|
|
@@ -1273,7 +1876,7 @@ function createExtension(deps = defaultDeps) {
|
|
|
1273
1876
|
pi.registerTool({
|
|
1274
1877
|
name: "avenor_spawn",
|
|
1275
1878
|
label: "Avenor Spawn",
|
|
1276
|
-
description: "Dispatch an agent run via avenor. Blocks by default, showing live progress. Set wait=false for fire-and-forget.",
|
|
1879
|
+
description: "Dispatch an agent run via avenor. Optional thinking accepts off, minimal, low, medium, high, xhigh, or max; unsupported backends reject explicit values. Blocks by default, showing live progress. Set wait=false for fire-and-forget.",
|
|
1277
1880
|
promptSnippet: "Dispatch sub-agent runs via avenor (spawn, status, result, inspect, events, follow-up, shutdown)",
|
|
1278
1881
|
promptGuidelines: [
|
|
1279
1882
|
"Use avenor_spawn to delegate well-defined tasks to sub-agents.",
|
|
@@ -1285,14 +1888,25 @@ function createExtension(deps = defaultDeps) {
|
|
|
1285
1888
|
"Use avenor_shutdown when done delegating work."
|
|
1286
1889
|
],
|
|
1287
1890
|
parameters: Type.Object({
|
|
1288
|
-
agent: Type.String({ description: "
|
|
1891
|
+
agent: Type.Optional(Type.String({ description: "Optional agent name; omission uses the supplied model or runtime defaults" })),
|
|
1289
1892
|
prompt: Type.Optional(Type.String({ description: "Prompt text" })),
|
|
1290
1893
|
prompt_file: Type.Optional(Type.String({ description: "Path to prompt file" })),
|
|
1291
1894
|
dir: Type.Optional(Type.String({ description: "Working directory (defaults to session directory)" })),
|
|
1292
1895
|
label: Type.Optional(Type.String({ description: "Human-readable label for the run" })),
|
|
1293
1896
|
timeout: Type.Optional(Type.String({ description: "Timeout duration (e.g. 3600s)" })),
|
|
1294
1897
|
model: Type.Optional(Type.String({ description: "Model override" })),
|
|
1295
|
-
|
|
1898
|
+
thinking: Type.Optional(Type.Union([
|
|
1899
|
+
Type.Literal("off"),
|
|
1900
|
+
Type.Literal("minimal"),
|
|
1901
|
+
Type.Literal("low"),
|
|
1902
|
+
Type.Literal("medium"),
|
|
1903
|
+
Type.Literal("high"),
|
|
1904
|
+
Type.Literal("xhigh"),
|
|
1905
|
+
Type.Literal("max")
|
|
1906
|
+
], { description: "Thinking level; omission uses the backend default" })),
|
|
1907
|
+
backend: Type.Optional(Type.String({ description: "Backend override (defaults to pi for direct mode)" })),
|
|
1908
|
+
roster_file: Type.Optional(Type.String({ description: "Path to the roster map" })),
|
|
1909
|
+
roster_entry: Type.Optional(Type.String({ description: "Roster entry to select" })),
|
|
1296
1910
|
server_url: Type.Optional(Type.String({ description: "Backend server URL" })),
|
|
1297
1911
|
supervisor_id: Type.Optional(Type.String({ description: "Reuse an existing supervisor by socket path" })),
|
|
1298
1912
|
wait: Type.Optional(Type.Boolean({ description: "Block until complete (default true)" }))
|
|
@@ -1302,24 +1916,39 @@ function createExtension(deps = defaultDeps) {
|
|
|
1302
1916
|
type: "text",
|
|
1303
1917
|
text: "Cancelled"
|
|
1304
1918
|
}] };
|
|
1919
|
+
validateSpawnSelection({
|
|
1920
|
+
agent: params.agent,
|
|
1921
|
+
model: params.model,
|
|
1922
|
+
backend: params.backend,
|
|
1923
|
+
roster_file: params.roster_file,
|
|
1924
|
+
roster_entry: params.roster_entry
|
|
1925
|
+
});
|
|
1305
1926
|
const wait = params.wait ?? true;
|
|
1306
1927
|
const dir = params.dir ?? ctx.cwd;
|
|
1307
|
-
const label = params.label ?? `${params.agent}-${Date.now()}`;
|
|
1308
|
-
const
|
|
1928
|
+
const label = params.label ?? `${params.agent ?? params.roster_entry ?? params.model ?? "avenor"}-${Date.now()}`;
|
|
1929
|
+
const rosterMode = Boolean(params.roster_entry);
|
|
1930
|
+
const backend = rosterMode ? params.backend : params.backend ?? "pi";
|
|
1309
1931
|
const entries = ctx.sessionManager?.getEntries?.();
|
|
1310
|
-
const agentProfile = backend === "pi" ? resolveAgentProfile(entries) : void 0;
|
|
1932
|
+
const agentProfile = rosterMode || backend === "pi" ? resolveAgentProfile(entries) : void 0;
|
|
1933
|
+
const hostParams = {
|
|
1934
|
+
...params,
|
|
1935
|
+
backend
|
|
1936
|
+
};
|
|
1311
1937
|
const result = await deps.spawnTool({
|
|
1312
|
-
agent: params.agent,
|
|
1313
|
-
prompt: params.prompt,
|
|
1314
|
-
promptFile: params.prompt_file,
|
|
1938
|
+
...params.agent !== void 0 ? { agent: params.agent } : {},
|
|
1939
|
+
...params.prompt !== void 0 ? { prompt: params.prompt } : {},
|
|
1940
|
+
...params.prompt_file !== void 0 ? { promptFile: params.prompt_file } : {},
|
|
1315
1941
|
label,
|
|
1316
1942
|
dir,
|
|
1317
|
-
timeout: params.timeout,
|
|
1318
|
-
model: params.model,
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1943
|
+
...params.timeout !== void 0 ? { timeout: params.timeout } : {},
|
|
1944
|
+
...params.model !== void 0 ? { model: params.model } : {},
|
|
1945
|
+
...params.thinking !== void 0 ? { thinking: params.thinking } : {},
|
|
1946
|
+
...backend !== void 0 ? { backend } : {},
|
|
1947
|
+
...params.roster_file !== void 0 ? { rosterFile: params.roster_file } : {},
|
|
1948
|
+
...params.roster_entry !== void 0 ? { rosterEntry: params.roster_entry } : {},
|
|
1949
|
+
...agentProfile !== void 0 ? { agentProfile } : {},
|
|
1950
|
+
...params.server_url !== void 0 ? { serverUrl: params.server_url } : {},
|
|
1951
|
+
...params.supervisor_id !== void 0 ? { supervisorId: params.supervisor_id } : {}
|
|
1323
1952
|
});
|
|
1324
1953
|
const supervisorId = statusSupervisorId(params.supervisor_id, result.supervisor_id);
|
|
1325
1954
|
const runRef = {
|
|
@@ -1338,7 +1967,8 @@ function createExtension(deps = defaultDeps) {
|
|
|
1338
1967
|
}],
|
|
1339
1968
|
details: {
|
|
1340
1969
|
run_id: result.run_id,
|
|
1341
|
-
label
|
|
1970
|
+
label,
|
|
1971
|
+
...spawnIdentityMetadata(hostParams)
|
|
1342
1972
|
}
|
|
1343
1973
|
};
|
|
1344
1974
|
onUpdate?.({
|
|
@@ -1359,7 +1989,10 @@ function createExtension(deps = defaultDeps) {
|
|
|
1359
1989
|
type: "text",
|
|
1360
1990
|
text: `Monitoring of "${label}" (run_id: ${result.run_id}) was interrupted. Use avenor_status or avenor_inspect to check.`
|
|
1361
1991
|
}],
|
|
1362
|
-
details: {
|
|
1992
|
+
details: {
|
|
1993
|
+
run_id: result.run_id,
|
|
1994
|
+
...spawnIdentityMetadata(hostParams)
|
|
1995
|
+
}
|
|
1363
1996
|
};
|
|
1364
1997
|
}
|
|
1365
1998
|
const status = waitResult.status;
|
|
@@ -1370,7 +2003,8 @@ function createExtension(deps = defaultDeps) {
|
|
|
1370
2003
|
}],
|
|
1371
2004
|
details: {
|
|
1372
2005
|
status: "running",
|
|
1373
|
-
run_id: result.run_id
|
|
2006
|
+
run_id: result.run_id,
|
|
2007
|
+
...spawnIdentityMetadata(hostParams)
|
|
1374
2008
|
}
|
|
1375
2009
|
};
|
|
1376
2010
|
upsertTrackedRun(runRef, status, wait);
|
|
@@ -1390,7 +2024,8 @@ function createExtension(deps = defaultDeps) {
|
|
|
1390
2024
|
status: "waiting",
|
|
1391
2025
|
run_id: result.run_id,
|
|
1392
2026
|
session_id: status.session_id,
|
|
1393
|
-
...status.pending_permission && { pending_permission: status.pending_permission }
|
|
2027
|
+
...status.pending_permission && { pending_permission: status.pending_permission },
|
|
2028
|
+
...spawnIdentityMetadata(hostParams, status)
|
|
1394
2029
|
}
|
|
1395
2030
|
};
|
|
1396
2031
|
}
|
|
@@ -1399,6 +2034,15 @@ function createExtension(deps = defaultDeps) {
|
|
|
1399
2034
|
runId: result.run_id,
|
|
1400
2035
|
supervisorId
|
|
1401
2036
|
}, status);
|
|
2037
|
+
emitAvenorEvent(pi.events, CHANNEL_RUN_TERMINAL, createRunTerminalPayload({
|
|
2038
|
+
runId: result.run_id,
|
|
2039
|
+
supervisorId,
|
|
2040
|
+
runtimeId: status.runtime_id ?? runRef.runtimeId,
|
|
2041
|
+
label,
|
|
2042
|
+
agent: params.agent,
|
|
2043
|
+
status: status.status,
|
|
2044
|
+
backend: status.backend
|
|
2045
|
+
}));
|
|
1402
2046
|
trackedRuns.delete(result.run_id);
|
|
1403
2047
|
return {
|
|
1404
2048
|
content: [{
|
|
@@ -1412,7 +2056,8 @@ function createExtension(deps = defaultDeps) {
|
|
|
1412
2056
|
status: status.status,
|
|
1413
2057
|
run_id: result.run_id,
|
|
1414
2058
|
session_id: status.session_id,
|
|
1415
|
-
final_output: finalOutput
|
|
2059
|
+
final_output: finalOutput,
|
|
2060
|
+
...spawnIdentityMetadata(hostParams, status)
|
|
1416
2061
|
}
|
|
1417
2062
|
};
|
|
1418
2063
|
}
|
|
@@ -1423,13 +2068,14 @@ function createExtension(deps = defaultDeps) {
|
|
|
1423
2068
|
}],
|
|
1424
2069
|
details: {
|
|
1425
2070
|
status: status.status,
|
|
1426
|
-
run_id: result.run_id
|
|
2071
|
+
run_id: result.run_id,
|
|
2072
|
+
...spawnIdentityMetadata(hostParams, status)
|
|
1427
2073
|
}
|
|
1428
2074
|
};
|
|
1429
2075
|
},
|
|
1430
2076
|
renderCall(args, theme) {
|
|
1431
2077
|
const agent = theme.fg("toolTitle", theme.bold("avenor_spawn "));
|
|
1432
|
-
const agentName = theme.fg("accent", args.agent);
|
|
2078
|
+
const agentName = theme.fg("accent", args.agent ?? args.roster_entry ?? args.model ?? "roster");
|
|
1433
2079
|
const label = args.label ? theme.fg("dim", ` "${args.label}"`) : "";
|
|
1434
2080
|
const wait = args.wait === false ? theme.fg("warning", " [async]") : "";
|
|
1435
2081
|
return new Text(agent + agentName + label + wait, 0, 0);
|
|
@@ -1474,6 +2120,15 @@ function createExtension(deps = defaultDeps) {
|
|
|
1474
2120
|
}],
|
|
1475
2121
|
details: result
|
|
1476
2122
|
};
|
|
2123
|
+
},
|
|
2124
|
+
renderCall(args, theme, context) {
|
|
2125
|
+
return renderStatusCall(context.args, theme);
|
|
2126
|
+
},
|
|
2127
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
2128
|
+
return renderStatusResult(result, {
|
|
2129
|
+
expanded,
|
|
2130
|
+
isPartial
|
|
2131
|
+
}, theme, context.args);
|
|
1477
2132
|
}
|
|
1478
2133
|
});
|
|
1479
2134
|
pi.registerTool({
|
|
@@ -1500,8 +2155,17 @@ function createExtension(deps = defaultDeps) {
|
|
|
1500
2155
|
signal
|
|
1501
2156
|
});
|
|
1502
2157
|
completed = result.ready;
|
|
1503
|
-
if (completed)
|
|
1504
|
-
|
|
2158
|
+
if (completed) {
|
|
2159
|
+
emitAvenorEvent(pi.events, CHANNEL_RUN_TERMINAL, createRunTerminalPayload({
|
|
2160
|
+
runId: params.run_id,
|
|
2161
|
+
supervisorId: tracked?.supervisorId ?? params.supervisor_id,
|
|
2162
|
+
runtimeId: tracked?.runtimeId ?? result.runtime_id,
|
|
2163
|
+
label: tracked?.label ?? result.label ?? params.run_id,
|
|
2164
|
+
agent: tracked?.agent ?? "unknown",
|
|
2165
|
+
status: result.status ?? "done"
|
|
2166
|
+
}));
|
|
2167
|
+
trackedRuns.delete(params.run_id);
|
|
2168
|
+
} else if (tracked && result.status === "waiting") tracked.permissionNotified = true;
|
|
1505
2169
|
return {
|
|
1506
2170
|
content: [{
|
|
1507
2171
|
type: "text",
|
|
@@ -1512,6 +2176,15 @@ function createExtension(deps = defaultDeps) {
|
|
|
1512
2176
|
} finally {
|
|
1513
2177
|
if (!completed && tracked && trackedRuns.get(params.run_id) === tracked) tracked.blocking = previousBlocking ?? false;
|
|
1514
2178
|
}
|
|
2179
|
+
},
|
|
2180
|
+
renderCall(args, theme, context) {
|
|
2181
|
+
return renderResultCall(context.args, theme);
|
|
2182
|
+
},
|
|
2183
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
2184
|
+
return renderResultResult(result, {
|
|
2185
|
+
expanded,
|
|
2186
|
+
isPartial
|
|
2187
|
+
}, theme, context.args);
|
|
1515
2188
|
}
|
|
1516
2189
|
});
|
|
1517
2190
|
pi.registerTool({
|
|
@@ -1538,6 +2211,15 @@ function createExtension(deps = defaultDeps) {
|
|
|
1538
2211
|
}],
|
|
1539
2212
|
details: payload
|
|
1540
2213
|
};
|
|
2214
|
+
},
|
|
2215
|
+
renderCall(args, theme, context) {
|
|
2216
|
+
return renderInspectCall(context.args, theme);
|
|
2217
|
+
},
|
|
2218
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
2219
|
+
return renderInspectResult(result, {
|
|
2220
|
+
expanded,
|
|
2221
|
+
isPartial
|
|
2222
|
+
}, theme, context.args);
|
|
1541
2223
|
}
|
|
1542
2224
|
});
|
|
1543
2225
|
pi.registerTool({
|
|
@@ -1566,6 +2248,15 @@ function createExtension(deps = defaultDeps) {
|
|
|
1566
2248
|
}],
|
|
1567
2249
|
details: result
|
|
1568
2250
|
};
|
|
2251
|
+
},
|
|
2252
|
+
renderCall(args, theme, context) {
|
|
2253
|
+
return renderAnswerPermissionCall(context.args, theme);
|
|
2254
|
+
},
|
|
2255
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
2256
|
+
return renderAnswerPermissionResult(result, {
|
|
2257
|
+
expanded,
|
|
2258
|
+
isPartial
|
|
2259
|
+
}, theme, context.args);
|
|
1569
2260
|
}
|
|
1570
2261
|
});
|
|
1571
2262
|
pi.registerTool({
|
|
@@ -1613,6 +2304,15 @@ function createExtension(deps = defaultDeps) {
|
|
|
1613
2304
|
}],
|
|
1614
2305
|
details: result
|
|
1615
2306
|
};
|
|
2307
|
+
},
|
|
2308
|
+
renderCall(args, theme, context) {
|
|
2309
|
+
return renderFollowUpCall(context.args, theme);
|
|
2310
|
+
},
|
|
2311
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
2312
|
+
return renderFollowUpResult(result, {
|
|
2313
|
+
expanded,
|
|
2314
|
+
isPartial
|
|
2315
|
+
}, theme, context.args);
|
|
1616
2316
|
}
|
|
1617
2317
|
});
|
|
1618
2318
|
pi.registerTool({
|
|
@@ -1639,6 +2339,15 @@ function createExtension(deps = defaultDeps) {
|
|
|
1639
2339
|
}],
|
|
1640
2340
|
details: result
|
|
1641
2341
|
};
|
|
2342
|
+
},
|
|
2343
|
+
renderCall(args, theme, context) {
|
|
2344
|
+
return renderEventsCall(context.args, theme);
|
|
2345
|
+
},
|
|
2346
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
2347
|
+
return renderEventsResult(result, {
|
|
2348
|
+
expanded,
|
|
2349
|
+
isPartial
|
|
2350
|
+
}, theme, context.args);
|
|
1642
2351
|
}
|
|
1643
2352
|
});
|
|
1644
2353
|
pi.registerTool({
|
|
@@ -1663,12 +2372,22 @@ function createExtension(deps = defaultDeps) {
|
|
|
1663
2372
|
}],
|
|
1664
2373
|
details: result
|
|
1665
2374
|
};
|
|
2375
|
+
},
|
|
2376
|
+
renderCall(args, theme, context) {
|
|
2377
|
+
return renderShutdownCall(context.args, theme);
|
|
2378
|
+
},
|
|
2379
|
+
renderResult(result, { expanded, isPartial }, theme, context) {
|
|
2380
|
+
return renderShutdownResult(result, {
|
|
2381
|
+
expanded,
|
|
2382
|
+
isPartial
|
|
2383
|
+
}, theme, context.args);
|
|
1666
2384
|
}
|
|
1667
2385
|
});
|
|
1668
2386
|
pi.registerCommand("avenor-status", {
|
|
1669
2387
|
description: "Show status of all avenor runs",
|
|
1670
2388
|
handler: async (_args, ctx) => {
|
|
1671
2389
|
const entries = await pollRuns();
|
|
2390
|
+
updateWidget(entries);
|
|
1672
2391
|
if (entries.length === 0) {
|
|
1673
2392
|
ctx.ui.notify("No active avenor runs", "info");
|
|
1674
2393
|
return;
|
|
@@ -1676,12 +2395,28 @@ function createExtension(deps = defaultDeps) {
|
|
|
1676
2395
|
ctx.ui.setWidget("avenor-status-detail", entries.map((entry) => formatRunLine(entry)));
|
|
1677
2396
|
}
|
|
1678
2397
|
});
|
|
2398
|
+
pi.registerCommand("avenor-errors", {
|
|
2399
|
+
description: "Show and clear recent Avenor polling errors",
|
|
2400
|
+
handler: async (_args, ctx) => {
|
|
2401
|
+
if (pollingErrors.length === 0) {
|
|
2402
|
+
ctx.ui.setWidget("avenor-errors", void 0);
|
|
2403
|
+
ctx.ui.notify("No recorded avenor polling errors", "info");
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2406
|
+
const errorLines = pollingErrors.map((error) => {
|
|
2407
|
+
const run = error.runId ? ` [${error.runId}]` : "";
|
|
2408
|
+
return `${new Date(error.timestamp).toLocaleTimeString()} ${error.message}${run}: ${error.error}`;
|
|
2409
|
+
});
|
|
2410
|
+
ctx.ui.setWidget("avenor-errors", errorLines);
|
|
2411
|
+
clearPollingErrors();
|
|
2412
|
+
}
|
|
2413
|
+
});
|
|
1679
2414
|
pi.registerCommand("avenor-watch", {
|
|
1680
2415
|
description: "Open a live run inspector by run ID, label, or agent name",
|
|
1681
2416
|
getArgumentCompletions: (prefix) => {
|
|
1682
|
-
const items = Array.from(trackedRuns.values()).filter((run) => run.label.startsWith(prefix) || run.agent
|
|
2417
|
+
const items = Array.from(trackedRuns.values()).filter((run) => run.label.startsWith(prefix) || run.agent?.startsWith(prefix) || run.runId.startsWith(prefix)).map((run) => ({
|
|
1683
2418
|
value: run.runId,
|
|
1684
|
-
label: `${sanitizeText(run.label)} (${sanitizeText(run.agent)}, ${sanitizeText(run.runId).slice(0, 8)})`
|
|
2419
|
+
label: `${sanitizeText(run.label)} (${sanitizeText(run.agent ?? "roster")}, ${sanitizeText(run.runId).slice(0, 8)})`
|
|
1685
2420
|
}));
|
|
1686
2421
|
return items.length > 0 ? items : null;
|
|
1687
2422
|
},
|
|
@@ -1703,9 +2438,9 @@ function createExtension(deps = defaultDeps) {
|
|
|
1703
2438
|
pi.registerCommand("avenor-cancel", {
|
|
1704
2439
|
description: "Cancel a running avenor sub-agent",
|
|
1705
2440
|
getArgumentCompletions: (prefix) => {
|
|
1706
|
-
const items = Array.from(trackedRuns.values()).filter((run) => run.label.startsWith(prefix) || run.agent
|
|
2441
|
+
const items = Array.from(trackedRuns.values()).filter((run) => run.label.startsWith(prefix) || run.agent?.startsWith(prefix) || run.runId.startsWith(prefix)).map((run) => ({
|
|
1707
2442
|
value: run.runId,
|
|
1708
|
-
label: `${sanitizeText(run.label)} (${sanitizeText(run.agent)}, ${sanitizeText(run.runId).slice(0, 8)})`
|
|
2443
|
+
label: `${sanitizeText(run.label)} (${sanitizeText(run.agent ?? "roster")}, ${sanitizeText(run.runId).slice(0, 8)})`
|
|
1709
2444
|
}));
|
|
1710
2445
|
return items.length > 0 ? items : null;
|
|
1711
2446
|
},
|
|
@@ -1740,4 +2475,4 @@ function createExtension(deps = defaultDeps) {
|
|
|
1740
2475
|
}
|
|
1741
2476
|
var src_default = createExtension();
|
|
1742
2477
|
//#endregion
|
|
1743
|
-
export { buildCompletionText, buildInspectPayload, compactWhitespace, createExtension, src_default as default, isTerminalStatus, renderStatusLines, statusSupervisorId };
|
|
2478
|
+
export { CHANNEL_POLL_COMPLETED, CHANNEL_POLL_ERROR, CHANNEL_RUN_TERMINAL, buildCompletionText, buildInspectPayload, compactWhitespace, createExtension, createPollCompletedPayload, createPollErrorPayload, createRunTerminalPayload, src_default as default, emitAvenorEvent, isTerminalStatus, onAvenorEvent, renderStatusLines, spawnIdentityMetadata, statusSupervisorId };
|