@dreb/coding-agent 2.46.0 → 2.48.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 +4 -2
- package/dist/core/agent-session.d.ts +22 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +109 -20
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +7 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +10 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +35 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/k3-context-tier.d.ts +67 -0
- package/dist/core/k3-context-tier.d.ts.map +1 -0
- package/dist/core/k3-context-tier.js +76 -0
- package/dist/core/k3-context-tier.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +6 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +38 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/tools/ask-user.d.ts +45 -0
- package/dist/core/tools/ask-user.d.ts.map +1 -0
- package/dist/core/tools/ask-user.js +210 -0
- package/dist/core/tools/ask-user.js.map +1 -0
- package/dist/core/tools/index.d.ts +11 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +8 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/modes/interactive/components/ask-user.d.ts +59 -0
- package/dist/modes/interactive/components/ask-user.d.ts.map +1 -0
- package/dist/modes/interactive/components/ask-user.js +240 -0
- package/dist/modes/interactive/components/ask-user.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +12 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +99 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts +16 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +112 -59
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +24 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/docs/dashboard.md +1 -1
- package/docs/extensions.md +33 -3
- package/docs/providers.md +1 -0
- package/docs/rpc.md +46 -6
- package/docs/session.md +1 -1
- package/package.json +1 -1
|
@@ -855,77 +855,105 @@ export async function navigateTreeForRpc(session, targetId, options) {
|
|
|
855
855
|
...(result.editorText !== undefined ? { editorText: result.editorText } : {}),
|
|
856
856
|
};
|
|
857
857
|
}
|
|
858
|
-
|
|
859
|
-
* Run in RPC mode.
|
|
860
|
-
* Listens for JSON commands on stdin, outputs events and responses on stdout.
|
|
861
|
-
*/
|
|
862
|
-
export async function runRpcMode(session, modelFallbackMessage) {
|
|
863
|
-
takeOverStdout();
|
|
864
|
-
const output = (obj) => {
|
|
865
|
-
writeRawStdout(serializeJsonLine(obj));
|
|
866
|
-
};
|
|
867
|
-
const success = (id, command, data) => {
|
|
868
|
-
if (data === undefined) {
|
|
869
|
-
return { id, type: "response", command, success: true };
|
|
870
|
-
}
|
|
871
|
-
return { id, type: "response", command, success: true, data };
|
|
872
|
-
};
|
|
873
|
-
const error = (id, command, message) => {
|
|
874
|
-
return { id, type: "response", command, success: false, error: message };
|
|
875
|
-
};
|
|
876
|
-
if (session.sessionFile && session.messages.length > 0) {
|
|
877
|
-
const rehydratedCount = rehydrateBackgroundAgentsFromDisk(session.sessionFile);
|
|
878
|
-
if (rehydratedCount > 0) {
|
|
879
|
-
console.error(`[rpc] Rehydrated ${rehydratedCount} background subagent${rehydratedCount === 1 ? "" : "s"} from disk`);
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
// Pending extension UI requests waiting for response
|
|
883
|
-
const pendingExtensionRequests = new Map();
|
|
884
|
-
// Shutdown request flag
|
|
885
|
-
let shutdownRequested = false;
|
|
886
|
-
let dailyCostTracker;
|
|
887
|
-
let dailyCostTrackerPrimed = false;
|
|
858
|
+
export function createRpcExtensionUIContext(output, pendingExtensionRequests, onAskStop = () => { }) {
|
|
888
859
|
/** Helper for dialog methods with signal/timeout support */
|
|
889
|
-
function createDialogPromise(opts, defaultValue, request, parseResponse) {
|
|
860
|
+
function createDialogPromise(opts, defaultValue, request, parseResponse, onTimeout) {
|
|
890
861
|
if (opts?.signal?.aborted)
|
|
891
862
|
return Promise.resolve(defaultValue);
|
|
892
863
|
const id = crypto.randomUUID();
|
|
893
864
|
return new Promise((resolve, reject) => {
|
|
894
865
|
let timeoutId;
|
|
866
|
+
let settled = false;
|
|
895
867
|
const cleanup = () => {
|
|
868
|
+
if (settled)
|
|
869
|
+
return false;
|
|
870
|
+
settled = true;
|
|
896
871
|
if (timeoutId)
|
|
897
872
|
clearTimeout(timeoutId);
|
|
898
873
|
opts?.signal?.removeEventListener("abort", onAbort);
|
|
899
874
|
pendingExtensionRequests.delete(id);
|
|
875
|
+
output({ type: "extension_ui_response_handled", id });
|
|
876
|
+
return true;
|
|
900
877
|
};
|
|
901
878
|
const onAbort = () => {
|
|
902
|
-
cleanup()
|
|
879
|
+
if (!cleanup())
|
|
880
|
+
return;
|
|
903
881
|
resolve(defaultValue);
|
|
904
882
|
};
|
|
905
883
|
opts?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
906
884
|
if (opts?.timeout) {
|
|
907
885
|
timeoutId = setTimeout(() => {
|
|
908
|
-
cleanup()
|
|
886
|
+
if (!cleanup())
|
|
887
|
+
return;
|
|
888
|
+
onTimeout?.();
|
|
909
889
|
resolve(defaultValue);
|
|
910
890
|
}, opts.timeout);
|
|
911
891
|
}
|
|
892
|
+
const rpcRequest = {
|
|
893
|
+
type: "extension_ui_request",
|
|
894
|
+
id,
|
|
895
|
+
...request,
|
|
896
|
+
};
|
|
912
897
|
pendingExtensionRequests.set(id, {
|
|
898
|
+
request: rpcRequest,
|
|
913
899
|
resolve: (response) => {
|
|
914
|
-
cleanup()
|
|
915
|
-
|
|
900
|
+
if (!cleanup())
|
|
901
|
+
return;
|
|
902
|
+
try {
|
|
903
|
+
resolve(parseResponse(response));
|
|
904
|
+
}
|
|
905
|
+
catch (error) {
|
|
906
|
+
reject(error);
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
reject: (error) => {
|
|
910
|
+
if (!cleanup())
|
|
911
|
+
return;
|
|
912
|
+
reject(error);
|
|
916
913
|
},
|
|
917
|
-
reject,
|
|
918
914
|
});
|
|
919
|
-
output(
|
|
915
|
+
output(rpcRequest);
|
|
920
916
|
});
|
|
921
917
|
}
|
|
922
918
|
/**
|
|
923
919
|
* Create an extension UI context that uses the RPC protocol.
|
|
924
920
|
*/
|
|
925
|
-
|
|
921
|
+
return {
|
|
926
922
|
select: (title, options, opts) => createDialogPromise(opts, undefined, { method: "select", title, options, timeout: opts?.timeout }, (r) => "cancelled" in r && r.cancelled ? undefined : "value" in r ? r.value : undefined),
|
|
927
923
|
confirm: (title, message, opts) => createDialogPromise(opts, false, { method: "confirm", title, message, timeout: opts?.timeout }, (r) => "cancelled" in r && r.cancelled ? false : "confirmed" in r ? r.confirmed : false),
|
|
928
924
|
input: (title, placeholder, opts) => createDialogPromise(opts, undefined, { method: "input", title, placeholder, timeout: opts?.timeout }, (r) => "cancelled" in r && r.cancelled ? undefined : "value" in r ? r.value : undefined),
|
|
925
|
+
ask: (request, opts) => createDialogPromise(opts, undefined, {
|
|
926
|
+
method: "ask",
|
|
927
|
+
title: request.title ?? "Question",
|
|
928
|
+
question: request.question,
|
|
929
|
+
options: request.options,
|
|
930
|
+
allowFreeText: request.allowFreeText,
|
|
931
|
+
multiSelect: request.multiSelect,
|
|
932
|
+
multiline: request.multiline,
|
|
933
|
+
timeout: opts?.timeout,
|
|
934
|
+
// Preserve the authoritative deadline across Dashboard reload,
|
|
935
|
+
// resync, and drill-in hydration instead of restarting the full
|
|
936
|
+
// duration whenever the question component remounts.
|
|
937
|
+
expiresAt: opts?.timeout ? Date.now() + opts.timeout : undefined,
|
|
938
|
+
}, (response) => {
|
|
939
|
+
if ("cancelled" in response && response.cancelled) {
|
|
940
|
+
onAskStop();
|
|
941
|
+
return undefined;
|
|
942
|
+
}
|
|
943
|
+
const selected = response.selected;
|
|
944
|
+
const customText = response.customText;
|
|
945
|
+
if (!Array.isArray(selected) || !selected.every((value) => typeof value === "string")) {
|
|
946
|
+
throw new Error("Invalid RPC ask response: selected must be an array of strings");
|
|
947
|
+
}
|
|
948
|
+
if (customText !== undefined && typeof customText !== "string") {
|
|
949
|
+
throw new Error("Invalid RPC ask response: customText must be a string");
|
|
950
|
+
}
|
|
951
|
+
if (selected.length === 0 && !customText?.trim()) {
|
|
952
|
+
onAskStop();
|
|
953
|
+
return undefined;
|
|
954
|
+
}
|
|
955
|
+
return { selected, customText };
|
|
956
|
+
}, onAskStop),
|
|
929
957
|
notify(message, type) {
|
|
930
958
|
// Fire and forget - no response needed
|
|
931
959
|
output({
|
|
@@ -1004,26 +1032,11 @@ export async function runRpcMode(session, modelFallbackMessage) {
|
|
|
1004
1032
|
// Host should track editor state locally if needed
|
|
1005
1033
|
return "";
|
|
1006
1034
|
},
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
if ("cancelled" in response && response.cancelled) {
|
|
1013
|
-
resolve(undefined);
|
|
1014
|
-
}
|
|
1015
|
-
else if ("value" in response) {
|
|
1016
|
-
resolve(response.value);
|
|
1017
|
-
}
|
|
1018
|
-
else {
|
|
1019
|
-
resolve(undefined);
|
|
1020
|
-
}
|
|
1021
|
-
},
|
|
1022
|
-
reject,
|
|
1023
|
-
});
|
|
1024
|
-
output({ type: "extension_ui_request", id, method: "editor", title, prefill });
|
|
1025
|
-
});
|
|
1026
|
-
},
|
|
1035
|
+
editor: (title, prefill) => createDialogPromise(undefined, undefined, { method: "editor", title, prefill }, (response) => "cancelled" in response && response.cancelled
|
|
1036
|
+
? undefined
|
|
1037
|
+
: "value" in response
|
|
1038
|
+
? response.value
|
|
1039
|
+
: undefined),
|
|
1027
1040
|
setEditorComponent() {
|
|
1028
1041
|
// Custom editor components not supported in RPC mode
|
|
1029
1042
|
},
|
|
@@ -1047,7 +1060,45 @@ export async function runRpcMode(session, modelFallbackMessage) {
|
|
|
1047
1060
|
setToolsExpanded(_expanded) {
|
|
1048
1061
|
// Tool expansion not supported in RPC mode - no TUI
|
|
1049
1062
|
},
|
|
1050
|
-
}
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
/** Cancel every pending dialog during RPC host teardown. */
|
|
1066
|
+
export function cancelPendingRpcExtensionRequests(pendingExtensionRequests) {
|
|
1067
|
+
for (const [id, pending] of [...pendingExtensionRequests]) {
|
|
1068
|
+
pending.resolve({ type: "extension_ui_response", id, cancelled: true });
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
export async function runRpcMode(session, modelFallbackMessage) {
|
|
1072
|
+
takeOverStdout();
|
|
1073
|
+
const output = (obj) => {
|
|
1074
|
+
writeRawStdout(serializeJsonLine(obj));
|
|
1075
|
+
};
|
|
1076
|
+
const success = (id, command, data) => {
|
|
1077
|
+
if (data === undefined) {
|
|
1078
|
+
return { id, type: "response", command, success: true };
|
|
1079
|
+
}
|
|
1080
|
+
return { id, type: "response", command, success: true, data };
|
|
1081
|
+
};
|
|
1082
|
+
const error = (id, command, message) => {
|
|
1083
|
+
return { id, type: "response", command, success: false, error: message };
|
|
1084
|
+
};
|
|
1085
|
+
if (session.sessionFile && session.messages.length > 0) {
|
|
1086
|
+
const rehydratedCount = rehydrateBackgroundAgentsFromDisk(session.sessionFile);
|
|
1087
|
+
if (rehydratedCount > 0) {
|
|
1088
|
+
console.error(`[rpc] Rehydrated ${rehydratedCount} background subagent${rehydratedCount === 1 ? "" : "s"} from disk`);
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
// Pending extension UI requests waiting for response. Keep the emitted
|
|
1092
|
+
// payload alongside its callbacks so recovery snapshots can reconstruct the
|
|
1093
|
+
// answer UI after a Dashboard reload or replay gap.
|
|
1094
|
+
const pendingExtensionRequests = new Map();
|
|
1095
|
+
// Shutdown request flag
|
|
1096
|
+
let shutdownRequested = false;
|
|
1097
|
+
let dailyCostTracker;
|
|
1098
|
+
let dailyCostTrackerPrimed = false;
|
|
1099
|
+
// Extension UI context uses the RPC protocol; built by a module-scope
|
|
1100
|
+
// factory so the dialog round trip is unit-testable (see createRpcExtensionUIContext).
|
|
1101
|
+
const createExtensionUIContext = () => createRpcExtensionUIContext(output, pendingExtensionRequests, () => void session.abort());
|
|
1051
1102
|
// Set up extensions with RPC-based UI context
|
|
1052
1103
|
await session.bindExtensions({
|
|
1053
1104
|
uiContext: createExtensionUIContext(),
|
|
@@ -1186,6 +1237,7 @@ export async function runRpcMode(session, modelFallbackMessage) {
|
|
|
1186
1237
|
state: getStateForRpc(session, modelFallbackMessage),
|
|
1187
1238
|
messages: getDashboardMessagesForRpc(session),
|
|
1188
1239
|
backgroundAgents: getBackgroundAgents().map(toRpcBackgroundAgentInfo),
|
|
1240
|
+
pendingExtensionUiRequests: [...pendingExtensionRequests.values()].map(({ request }) => request),
|
|
1189
1241
|
};
|
|
1190
1242
|
return success(id, "get_dashboard_snapshot", data);
|
|
1191
1243
|
}
|
|
@@ -1515,6 +1567,7 @@ export async function runRpcMode(session, modelFallbackMessage) {
|
|
|
1515
1567
|
*/
|
|
1516
1568
|
let detachInput = () => { };
|
|
1517
1569
|
async function shutdown() {
|
|
1570
|
+
cancelPendingRpcExtensionRequests(pendingExtensionRequests);
|
|
1518
1571
|
const currentRunner = session.extensionRunner;
|
|
1519
1572
|
if (currentRunner?.hasHandlers("session_shutdown")) {
|
|
1520
1573
|
await currentRunner.emit({ type: "session_shutdown" });
|