2026-04-05 21:09:33 -05:00
|
|
|
/**
|
|
|
|
|
* Agent CLI commands.
|
|
|
|
|
*
|
|
|
|
|
* Python reference: trustgraph-cli/trustgraph/cli/invoke_agent.py
|
|
|
|
|
*/
|
|
|
|
|
|
2026-06-02 00:22:04 -05:00
|
|
|
import { Effect } from "effect";
|
2026-06-06 10:33:10 -05:00
|
|
|
import * as Argument from "effect/unstable/cli/Argument";
|
|
|
|
|
import * as Command from "effect/unstable/cli/Command";
|
2026-06-11 06:29:29 -05:00
|
|
|
import { cliCommandError, withGatewayClient, type CliCommandError } from "./util.js";
|
|
|
|
|
|
|
|
|
|
function asRecord(value: unknown): Record<string, unknown> {
|
|
|
|
|
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
|
|
|
? value as Record<string, unknown>
|
|
|
|
|
: {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function stringProperty(source: unknown, key: string): string | undefined {
|
|
|
|
|
const value = asRecord(source)[key];
|
|
|
|
|
return typeof value === "string" ? value : undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function booleanProperty(source: unknown, key: string): boolean | undefined {
|
|
|
|
|
const value = asRecord(source)[key];
|
|
|
|
|
return typeof value === "boolean" ? value : undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function responseErrorMessage(source: unknown): string | undefined {
|
|
|
|
|
const error = asRecord(source).error;
|
|
|
|
|
if (typeof error === "string") return error;
|
|
|
|
|
return stringProperty(error, "message");
|
|
|
|
|
}
|
2026-04-05 21:09:33 -05:00
|
|
|
|
2026-06-06 10:33:10 -05:00
|
|
|
export const agentCommand = Command.make("agent", {
|
|
|
|
|
question: Argument.string("question").pipe(Argument.withDescription("Question to ask")),
|
|
|
|
|
}, ({ question }) =>
|
2026-06-11 06:29:29 -05:00
|
|
|
withGatewayClient((client, opts) =>
|
2026-06-06 10:33:10 -05:00
|
|
|
Effect.gen(function* () {
|
2026-06-11 06:29:29 -05:00
|
|
|
let streamError: CliCommandError | undefined;
|
2026-04-05 21:09:33 -05:00
|
|
|
|
2026-06-11 06:29:29 -05:00
|
|
|
yield* client.runDispatchStream(
|
|
|
|
|
{
|
|
|
|
|
scope: "flow",
|
|
|
|
|
flow: opts.flow,
|
|
|
|
|
service: "agent",
|
|
|
|
|
request: {
|
2026-04-05 22:44:45 -05:00
|
|
|
question,
|
2026-06-11 06:29:29 -05:00
|
|
|
user: opts.user,
|
|
|
|
|
collection: "default",
|
|
|
|
|
streaming: true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
(chunk) => {
|
|
|
|
|
const resp = asRecord(chunk.response);
|
|
|
|
|
const chunkType = stringProperty(resp, "chunk_type");
|
|
|
|
|
const error = chunkType === "error" ? responseErrorMessage(resp) ?? "Unknown agent error" : responseErrorMessage(resp);
|
|
|
|
|
if (error !== undefined) {
|
|
|
|
|
streamError = cliCommandError("agent", error);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const content = stringProperty(resp, "content") ?? "";
|
|
|
|
|
const messageComplete = booleanProperty(resp, "end_of_message") === true;
|
|
|
|
|
const dialogComplete = chunk.complete === true || booleanProperty(resp, "end_of_dialog") === true;
|
|
|
|
|
|
|
|
|
|
if (chunkType === "thought" || chunkType === "observation") {
|
|
|
|
|
if (content.length > 0) process.stderr.write(content);
|
|
|
|
|
} else if (chunkType === "answer" || chunkType === "final-answer") {
|
|
|
|
|
if (content.length > 0) process.stdout.write(content);
|
|
|
|
|
if (messageComplete || dialogComplete) process.stdout.write("\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dialogComplete;
|
|
|
|
|
},
|
|
|
|
|
{ timeoutMs: 120_000, retries: 2 },
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (streamError !== undefined) {
|
|
|
|
|
return yield* streamError;
|
|
|
|
|
}
|
2026-06-06 10:33:10 -05:00
|
|
|
}),
|
|
|
|
|
),
|
|
|
|
|
).pipe(Command.withDescription("Ask the TrustGraph agent a question"));
|