add update-state cmd

This commit is contained in:
Ramnique Singh 2025-11-18 20:38:14 +05:30
parent 7d4484e7c0
commit f6019a4fde
2 changed files with 79 additions and 19 deletions

View file

@ -4,28 +4,29 @@ import { hideBin } from 'yargs/helpers';
import { app } from '../dist/app.js';
yargs(hideBin(process.argv))
.command(
"$0",
"Run rowboatx",
(y) => y
.option("agent", {
type: "string",
description: "The agent to run",
default: "copilot",
})
.option("run_id", {
type: "string",
description: "Continue an existing run",
})
.option("input", {
type: "string",
description: "The input to the agent",
})
.option("no-interactive", {
type: "boolean",
description: "Do not interact with the user",
default: false,
}),
.option("agent", {
type: "string",
description: "The agent to run",
default: "copilot",
})
.option("run_id", {
type: "string",
description: "Continue an existing run",
})
.option("input", {
type: "string",
description: "The input to the agent",
})
.option("no-interactive", {
type: "boolean",
description: "Do not interact with the user",
default: false,
}),
(argv) => {
app({
agent: argv.agent,
@ -35,4 +36,20 @@ yargs(hideBin(process.argv))
});
}
)
.command(
"update-state <agent> <run_id>",
"Update state for a run",
(y) => y
.positional("agent", {
type: "string",
description: "The agent to run",
})
.positional("run_id", {
type: "string",
description: "The run id to update",
}),
(argv) => {
updateState(argv.agent, argv.run_id);
}
)
.parse();

View file

@ -9,6 +9,27 @@ import { createInterface, Interface } from "node:readline/promises";
import { ToolCallPart } from "./application/entities/message.js";
import { z } from "zod";
export async function updateState(agent: string, runId: string) {
const state = new AgentState(agent, runId);
// If running in a TTY, read run events from stdin line-by-line
if (!input.isTTY) {
return;
}
const rl = createInterface({ input, crlfDelay: Infinity });
try {
for await (const line of rl) {
if (line.trim() === "") {
continue;
}
const event = RunEvent.parse(JSON.parse(line));
state.ingestAndLog(event);
}
} finally {
rl.close();
}
}
export async function app(opts: {
agent: string;
runId?: string;
@ -16,7 +37,7 @@ export async function app(opts: {
noInteractive?: boolean;
}) {
const renderer = new StreamRenderer();
const state = new AgentState(opts.agent);
const state = new AgentState(opts.agent, opts.runId);
// load existing and assemble state if required
let runId = opts.runId;
@ -45,11 +66,15 @@ export async function app(opts: {
if (!opts.noInteractive) {
rl = createInterface({ input, output });
}
let inputConsumed = false;
try {
while (true) {
// ask for pending tool permissions
for (const perm of Object.values(state.getPendingPermissions())) {
if (opts.noInteractive) {
return;
}
const response = await getToolCallPermission(perm.toolCall, rl!);
state.ingestAndLog({
type: "tool-permission-response",
@ -61,6 +86,9 @@ export async function app(opts: {
// ask for pending human input
for (const ask of Object.values(state.getPendingAskHumans())) {
if (opts.noInteractive) {
return;
}
const response = await getAskHumanResponse(ask.query, rl!);
state.ingestAndLog({
type: "ask-human-response",
@ -80,6 +108,21 @@ export async function app(opts: {
// if nothing pending, get user input
if (state.getPendingPermissions().length === 0 && state.getPendingAskHumans().length === 0) {
if (opts.input && !inputConsumed) {
state.ingestAndLog({
type: "message",
message: {
role: "user",
content: opts.input,
},
subflow: [],
});
inputConsumed = true;
continue;
}
if (opts.noInteractive) {
return;
}
const response = await getUserInput(rl!);
state.ingestAndLog({
type: "message",