@pantheon.ai/agents 0.0.17 → 0.1.1
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 +8 -4
- package/dist/index.js +1142 -649
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -395,7 +395,7 @@ var require_cli_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
395
395
|
|
|
396
396
|
//#endregion
|
|
397
397
|
//#region package.json
|
|
398
|
-
var version$1 = "0.
|
|
398
|
+
var version$1 = "0.1.1";
|
|
399
399
|
|
|
400
400
|
//#endregion
|
|
401
401
|
//#region src/schemas/task-list.ts
|
|
@@ -1601,14 +1601,17 @@ const githubAppInstallationSchema = z$1.object({
|
|
|
1601
1601
|
github_account_id: z$1.number(),
|
|
1602
1602
|
github_account_login: z$1.string(),
|
|
1603
1603
|
github_account_type: z$1.string(),
|
|
1604
|
-
org_id: z$1.string().nullable()
|
|
1604
|
+
org_id: z$1.string().nullable(),
|
|
1605
|
+
allow_by_repo_permission: z$1.boolean()
|
|
1605
1606
|
});
|
|
1607
|
+
const githubAppInstallationUpdateSchema = z$1.object({ allow_by_repo_permission: z$1.boolean().optional() });
|
|
1606
1608
|
const githubAppInstallationAdminItemSchema = z$1.object({
|
|
1607
1609
|
installation_id: z$1.number(),
|
|
1608
1610
|
github_account_id: z$1.number(),
|
|
1609
1611
|
github_account_login: z$1.string(),
|
|
1610
1612
|
github_account_type: z$1.string(),
|
|
1611
1613
|
org_id: z$1.string().nullable(),
|
|
1614
|
+
allow_by_repo_permission: z$1.boolean().optional(),
|
|
1612
1615
|
org_name: z$1.string().nullable(),
|
|
1613
1616
|
installer_user_id: z$1.string().nullable(),
|
|
1614
1617
|
installer_email: z$1.string().nullable(),
|
|
@@ -1633,6 +1636,7 @@ const githubAutoReviewConfigSchema = z$1.object({
|
|
|
1633
1636
|
});
|
|
1634
1637
|
const githubRepoSettingsConfigSchema = z$1.object({
|
|
1635
1638
|
allow_usernames: z$1.array(z$1.string()),
|
|
1639
|
+
allow_by_repo_permission: z$1.boolean().nullable().optional(),
|
|
1636
1640
|
auto_review: githubAutoReviewConfigSchema
|
|
1637
1641
|
});
|
|
1638
1642
|
const githubRepoSettingsPublicSchema = z$1.object({
|
|
@@ -1658,6 +1662,8 @@ const addOrganizationMember = defineApi(post`/api/v1/orgs/${"orgId"}/members`, t
|
|
|
1658
1662
|
const updateOrganizationMemberRole = defineApi(put`/api/v1/orgs/${"orgId"}/members/${"memberId"}`, typeOf(), organizationMemberSchema);
|
|
1659
1663
|
const removeOrganizationMember = defineApi(del`/api/v1/orgs/${"orgId"}/members/${"memberId"}`, null, z$1.object({ success: z$1.boolean() }));
|
|
1660
1664
|
const listOrganizationGithubInstallations = defineApi(get`/api/v1/orgs/${"orgId"}/github/installations`, null, z$1.array(githubAppInstallationSchema));
|
|
1665
|
+
const getOrganizationGithubInstallation = defineApi(get`/api/v1/orgs/${"orgId"}/github/installations/${"installationId"}`, null, githubAppInstallationSchema);
|
|
1666
|
+
const updateOrganizationGithubInstallation = defineApi(put`/api/v1/orgs/${"orgId"}/github/installations/${"installationId"}`, typeOf(), githubAppInstallationSchema);
|
|
1661
1667
|
const listOrgLinkedAccounts = defineApi(get`/api/v1/orgs/${"orgId"}/github/linked_accounts`, null, z$1.array(githubAppInstallationSchema));
|
|
1662
1668
|
const listOrgLinkedRepositories = defineApi(get`/api/v1/orgs/${"orgId"}/github/linked_repositories`, null, z$1.array(githubRepositorySchema));
|
|
1663
1669
|
const listOrganizationGithubRepositories = defineApi(get`/api/v1/orgs/${"orgId"}/github/installations/${"installationId"}/repos`, null, z$1.array(githubRepositorySchema));
|
|
@@ -17034,8 +17040,9 @@ async function getPantheonBranchInfo({ projectId, branchId }) {
|
|
|
17034
17040
|
}, null);
|
|
17035
17041
|
}
|
|
17036
17042
|
async function executeOnPantheon({ projectId, branchId, prompt, agent }) {
|
|
17043
|
+
const promptSequence = Array.isArray(prompt) ? prompt : [prompt];
|
|
17037
17044
|
return (await executor.execute(createProjectExploration, { projectId }, {
|
|
17038
|
-
shared_prompt_sequence:
|
|
17045
|
+
shared_prompt_sequence: promptSequence,
|
|
17039
17046
|
num_branches: 1,
|
|
17040
17047
|
agent,
|
|
17041
17048
|
parent_branch_id: branchId
|
|
@@ -17059,8 +17066,47 @@ function getTaskWorkingBranchId(task) {
|
|
|
17059
17066
|
return ("branch_id" in task ? task.branch_id : void 0) ?? task.base_branch_id;
|
|
17060
17067
|
}
|
|
17061
17068
|
|
|
17069
|
+
//#endregion
|
|
17070
|
+
//#region src/core/agent-config-utils.ts
|
|
17071
|
+
function normalizeRepoUrl(value) {
|
|
17072
|
+
return value.trim().replace(/\/+$/, "").replace(/\.git$/, "");
|
|
17073
|
+
}
|
|
17074
|
+
function toRawGitHubContentUrl(url) {
|
|
17075
|
+
if (!url.startsWith("https://github.com/")) return url;
|
|
17076
|
+
return url.replace("https://github.com/", "https://raw.githubusercontent.com/").replace("/blob/", "/");
|
|
17077
|
+
}
|
|
17078
|
+
function normalizeSkills(value) {
|
|
17079
|
+
if (Array.isArray(value)) return value.filter((item) => typeof item === "string");
|
|
17080
|
+
if (typeof value === "string") {
|
|
17081
|
+
try {
|
|
17082
|
+
const parsed = JSON.parse(value);
|
|
17083
|
+
if (Array.isArray(parsed)) return parsed.filter((item) => typeof item === "string");
|
|
17084
|
+
} catch {}
|
|
17085
|
+
return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
|
|
17086
|
+
}
|
|
17087
|
+
return [];
|
|
17088
|
+
}
|
|
17089
|
+
function normalizeSetupScriptSlug(setupScript, fallback) {
|
|
17090
|
+
if (typeof setupScript === "string" && setupScript.trim() !== "") return setupScript.trim();
|
|
17091
|
+
return fallback.trim();
|
|
17092
|
+
}
|
|
17093
|
+
function buildSetupScriptStep(options) {
|
|
17094
|
+
const prototypeRepoUrl = normalizeRepoUrl(options.prototypeRepoUrl);
|
|
17095
|
+
const setupScriptUrl = toRawGitHubContentUrl(`${prototypeRepoUrl}/blob/main/scripts/${`setup-${options.setupScriptSlug}.sh`}`);
|
|
17096
|
+
const skillsArg = JSON.stringify(options.skills.join(","));
|
|
17097
|
+
return `execute shell ${setupScriptUrl} ${options.role.trim()} ${skillsArg} ${prototypeRepoUrl}`;
|
|
17098
|
+
}
|
|
17099
|
+
|
|
17062
17100
|
//#endregion
|
|
17063
17101
|
//#region src/core/task-list.ts
|
|
17102
|
+
function buildTaskPromptSequence(config, taskPrompt) {
|
|
17103
|
+
return [buildSetupScriptStep({
|
|
17104
|
+
prototypeRepoUrl: config.prototype_url,
|
|
17105
|
+
setupScriptSlug: normalizeSetupScriptSlug(config.setup_script, config.execute_agent),
|
|
17106
|
+
role: config.role,
|
|
17107
|
+
skills: normalizeSkills(config.skills)
|
|
17108
|
+
}), taskPrompt];
|
|
17109
|
+
}
|
|
17064
17110
|
async function startTaskListLoop(agentName, { loopInterval = 5 }, logger) {
|
|
17065
17111
|
logger.info("Starting task list loop...");
|
|
17066
17112
|
let i = 0;
|
|
@@ -17134,11 +17180,12 @@ async function startPendingTask(provider, task, logger) {
|
|
|
17134
17180
|
logger.warn("Agent configuration not found for project %s. Skip starting tasks.", taskToStart.project_id);
|
|
17135
17181
|
return false;
|
|
17136
17182
|
}
|
|
17183
|
+
const promptSequence = buildTaskPromptSequence(config, taskToStart.task);
|
|
17137
17184
|
const taskBranch = await provider.startTask(taskToStart, async () => {
|
|
17138
17185
|
return await executeOnPantheon({
|
|
17139
17186
|
projectId: taskToStart.project_id,
|
|
17140
17187
|
branchId: taskToStart.base_branch_id,
|
|
17141
|
-
prompt:
|
|
17188
|
+
prompt: promptSequence,
|
|
17142
17189
|
agent: config.execute_agent
|
|
17143
17190
|
});
|
|
17144
17191
|
});
|
|
@@ -17538,17 +17585,6 @@ var WatchStepAggregator = class {
|
|
|
17538
17585
|
|
|
17539
17586
|
//#endregion
|
|
17540
17587
|
//#region src/core/index.ts
|
|
17541
|
-
function normalizeSkills(value) {
|
|
17542
|
-
if (Array.isArray(value)) return value.filter((item) => typeof item === "string");
|
|
17543
|
-
if (typeof value === "string") {
|
|
17544
|
-
try {
|
|
17545
|
-
const parsed = JSON.parse(value);
|
|
17546
|
-
if (Array.isArray(parsed)) return parsed.filter((item) => typeof item === "string");
|
|
17547
|
-
} catch {}
|
|
17548
|
-
return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
|
|
17549
|
-
}
|
|
17550
|
-
return [];
|
|
17551
|
-
}
|
|
17552
17588
|
function buildRebootstrapDiffPrompt(options) {
|
|
17553
17589
|
return `You must follow these instructions":
|
|
17554
17590
|
1. Clone the main branch from ${options.prototypeUrl} to a temporary directory (<pantheon-agents> in follow instructions references to this directory).
|
|
@@ -17635,75 +17671,21 @@ async function configAgent(name, options) {
|
|
|
17635
17671
|
}
|
|
17636
17672
|
options.rootBranchId = project.root_branch_id;
|
|
17637
17673
|
}
|
|
17638
|
-
|
|
17639
|
-
|
|
17640
|
-
|
|
17641
|
-
|
|
17642
|
-
|
|
17643
|
-
|
|
17644
|
-
|
|
17645
|
-
|
|
17646
|
-
3. Copy the <pantheon-agents>/agents/${options.role}/skills directory to \`<workspace>/.codex/skills\` if the source directory exists.
|
|
17647
|
-
${options.skills.length > 0 ? `4. Copy the pantheon-agents/skills/\{${options.skills.join(",")}} directory to \`<workspace>/.codex/skills/\`` : ""}
|
|
17648
|
-
|
|
17649
|
-
Validate <workspace>: check if files and directorys exists:
|
|
17650
|
-
- AGENTS.md
|
|
17651
|
-
- .codex/skills
|
|
17652
|
-
|
|
17653
|
-
**You MUST NOT generate AGENTS.md and skills by yourself if clone failed.**
|
|
17654
|
-
|
|
17655
|
-
Finally, outputs the first 5 lines of <workspace>/AGENTS.md and the skills list in <workspace>/.codex/skills
|
|
17656
|
-
`
|
|
17657
|
-
});
|
|
17658
|
-
let retried = 0;
|
|
17659
|
-
const maxRetries = 3;
|
|
17660
|
-
let i = 0;
|
|
17661
|
-
console.log(`Bootstrap branch created: ${branchId} ${branchLatestSnapId}. Waiting for ready... [poll interval = 10s]`);
|
|
17662
|
-
while (true) {
|
|
17663
|
-
await new Promise((resolve) => {
|
|
17664
|
-
setTimeout(resolve, 1e4);
|
|
17665
|
-
});
|
|
17666
|
-
const result = await getPantheonBranch({
|
|
17667
|
-
branchId,
|
|
17668
|
-
projectId: options.projectId,
|
|
17669
|
-
getOutputIfFinished: true
|
|
17670
|
-
}).then((result) => {
|
|
17671
|
-
retried = 0;
|
|
17672
|
-
return result;
|
|
17673
|
-
}).catch((reason) => {
|
|
17674
|
-
if (retried < maxRetries) {
|
|
17675
|
-
retried++;
|
|
17676
|
-
return { state: "others" };
|
|
17677
|
-
}
|
|
17678
|
-
throw new Error(`Failed to get bootstrap branch status. Retry ${retried} times. Last error: ${getErrorMessage$2(reason)}`);
|
|
17679
|
-
});
|
|
17680
|
-
if (result.state === "failed") {
|
|
17681
|
-
console.error("Bootstrap failed: " + result.error);
|
|
17682
|
-
process.exitCode = 1;
|
|
17683
|
-
return;
|
|
17684
|
-
}
|
|
17685
|
-
if (result.state === "succeed") {
|
|
17686
|
-
console.log("Bootstrap succeeded. Output is:");
|
|
17687
|
-
console.log(result.output);
|
|
17688
|
-
break;
|
|
17689
|
-
}
|
|
17690
|
-
console.log(`Bootstrap in progress... [${++i}]`);
|
|
17691
|
-
}
|
|
17692
|
-
await provider.setAgentConfig({
|
|
17693
|
-
project_id: options.projectId,
|
|
17694
|
-
base_branch_id: branchId,
|
|
17695
|
-
execute_agent: options.executeAgent,
|
|
17696
|
-
role: options.role,
|
|
17697
|
-
skills: options.skills,
|
|
17698
|
-
prototype_url: options.prototypeUrl
|
|
17699
|
-
});
|
|
17700
|
-
} else await provider.setAgentConfig({
|
|
17674
|
+
const rootBranchId = options.rootBranchId;
|
|
17675
|
+
if (!rootBranchId) {
|
|
17676
|
+
console.error("Unable to resolve root branch id.");
|
|
17677
|
+
process.exitCode = 1;
|
|
17678
|
+
return;
|
|
17679
|
+
}
|
|
17680
|
+
const setupScript = normalizeSetupScriptSlug(options.setupScript, options.executeAgent);
|
|
17681
|
+
await provider.setAgentConfig({
|
|
17701
17682
|
project_id: options.projectId,
|
|
17702
|
-
base_branch_id:
|
|
17683
|
+
base_branch_id: rootBranchId,
|
|
17703
17684
|
execute_agent: options.executeAgent,
|
|
17704
17685
|
role: options.role,
|
|
17705
17686
|
skills: options.skills,
|
|
17706
|
-
prototype_url: options.prototypeUrl
|
|
17687
|
+
prototype_url: options.prototypeUrl,
|
|
17688
|
+
setup_script: setupScript
|
|
17707
17689
|
});
|
|
17708
17690
|
console.log(`Agent ${name} configured successfully.`);
|
|
17709
17691
|
} finally {
|
|
@@ -17716,6 +17698,9 @@ async function reconfigAgentWithDeps(name, options, deps) {
|
|
|
17716
17698
|
const resolvedRole = options.role?.trim() || previousConfig.role;
|
|
17717
17699
|
if (options.role != null && !options.role.trim()) throw new Error("--role must be non-empty.");
|
|
17718
17700
|
const resolvedSkills = options.skills ?? normalizeSkills(previousConfig.skills);
|
|
17701
|
+
const previousSetupScript = normalizeSetupScriptSlug(previousConfig.setup_script, previousConfig.execute_agent);
|
|
17702
|
+
const resolvedSetupScript = options.setupScript?.trim() || previousSetupScript;
|
|
17703
|
+
if (options.setupScript != null && !options.setupScript.trim()) throw new Error("--setup-script must be non-empty.");
|
|
17719
17704
|
const { branch_id: nextBaseBranchId } = await deps.executeOnPantheonFn({
|
|
17720
17705
|
projectId: options.projectId,
|
|
17721
17706
|
branchId: previousConfig.base_branch_id,
|
|
@@ -17742,7 +17727,8 @@ async function reconfigAgentWithDeps(name, options, deps) {
|
|
|
17742
17727
|
execute_agent: previousConfig.execute_agent,
|
|
17743
17728
|
role: resolvedRole,
|
|
17744
17729
|
skills: resolvedSkills,
|
|
17745
|
-
prototype_url: previousConfig.prototype_url
|
|
17730
|
+
prototype_url: previousConfig.prototype_url,
|
|
17731
|
+
setup_script: resolvedSetupScript
|
|
17746
17732
|
});
|
|
17747
17733
|
return {
|
|
17748
17734
|
previousConfig,
|
|
@@ -17963,15 +17949,32 @@ function ensureEnv(keys) {
|
|
|
17963
17949
|
process.exitCode = 1;
|
|
17964
17950
|
return false;
|
|
17965
17951
|
}
|
|
17952
|
+
function normalizeOptional(value) {
|
|
17953
|
+
if (value == null) return void 0;
|
|
17954
|
+
const trimmed = value.trim();
|
|
17955
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
17956
|
+
}
|
|
17957
|
+
function resolvePantheonProjectId(projectIdArg) {
|
|
17958
|
+
return normalizeOptional(projectIdArg) ?? normalizeOptional(process.env.DEFAULT_PANTHEON_PROJECT_ID);
|
|
17959
|
+
}
|
|
17960
|
+
function resolvePantheonRootBranchId(rootBranchIdArg) {
|
|
17961
|
+
return normalizeOptional(rootBranchIdArg) ?? normalizeOptional(process.env.DEFAULT_PANTHEON_ROOT_BRANCH_ID);
|
|
17962
|
+
}
|
|
17966
17963
|
|
|
17967
17964
|
//#endregion
|
|
17968
17965
|
//#region src/cli/commands/add-task.ts
|
|
17969
|
-
function createAddTaskCommand(version) {
|
|
17970
|
-
return createCommand("add-task").version(version).description("Add a task to an agent").argument("<name>", "The name of the agent.").argument("<
|
|
17971
|
-
const [name,
|
|
17966
|
+
function createAddTaskCommand(version, deps = {}) {
|
|
17967
|
+
return createCommand("add-task").version(version).description("Add a task to an agent").argument("<name>", "The name of the agent.").argument("<task-prompt>", "The prompt of the task.").option("--project-id <project-id>", "The project id of the agent. Defaults to DEFAULT_PANTHEON_PROJECT_ID.").option("--parent-task-id <task-id>", "Parent task id for dependent tasks.").action(async function() {
|
|
17968
|
+
const [name, taskPrompt] = this.args;
|
|
17972
17969
|
const options = this.opts();
|
|
17970
|
+
const projectId = resolvePantheonProjectId(options.projectId);
|
|
17971
|
+
if (!projectId) {
|
|
17972
|
+
console.error("project-id is required (--project-id or DEFAULT_PANTHEON_PROJECT_ID).");
|
|
17973
|
+
process$1.exitCode = 1;
|
|
17974
|
+
return;
|
|
17975
|
+
}
|
|
17973
17976
|
if (!ensureEnv(["DATABASE_URL"])) return;
|
|
17974
|
-
await addTask(name, {
|
|
17977
|
+
await (deps.addTask ?? addTask)(name, {
|
|
17975
17978
|
projectId,
|
|
17976
17979
|
prompt: taskPrompt,
|
|
17977
17980
|
parentTaskId: options.parentTaskId
|
|
@@ -18024,19 +18027,103 @@ function createCancelTaskCommand(version) {
|
|
|
18024
18027
|
});
|
|
18025
18028
|
}
|
|
18026
18029
|
|
|
18030
|
+
//#endregion
|
|
18031
|
+
//#region src/cli/utils/parse.ts
|
|
18032
|
+
function parseCommaList(value) {
|
|
18033
|
+
return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
|
|
18034
|
+
}
|
|
18035
|
+
function parseUniqueCommaList(value) {
|
|
18036
|
+
const items = parseCommaList(value);
|
|
18037
|
+
const seen = /* @__PURE__ */ new Set();
|
|
18038
|
+
const result = [];
|
|
18039
|
+
for (const item of items) {
|
|
18040
|
+
if (seen.has(item)) continue;
|
|
18041
|
+
seen.add(item);
|
|
18042
|
+
result.push(item);
|
|
18043
|
+
}
|
|
18044
|
+
return result;
|
|
18045
|
+
}
|
|
18046
|
+
function isLeapYear(year) {
|
|
18047
|
+
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
18048
|
+
}
|
|
18049
|
+
function daysInMonth(year, month) {
|
|
18050
|
+
switch (month) {
|
|
18051
|
+
case 2: return isLeapYear(year) ? 29 : 28;
|
|
18052
|
+
case 4:
|
|
18053
|
+
case 6:
|
|
18054
|
+
case 9:
|
|
18055
|
+
case 11: return 30;
|
|
18056
|
+
default: return 31;
|
|
18057
|
+
}
|
|
18058
|
+
}
|
|
18059
|
+
function parseIsoTimestamp(value) {
|
|
18060
|
+
const invalid = (detail) => ({
|
|
18061
|
+
ok: false,
|
|
18062
|
+
error: `Invalid timestamp: ${JSON.stringify(value)}. ` + (detail ? `${detail} ` : "") + "Expected ISO-8601 date-time with timezone, e.g. 2026-02-12T00:00:00Z."
|
|
18063
|
+
});
|
|
18064
|
+
const input = value.trim();
|
|
18065
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?(Z|([+-])(\d{2}):(\d{2}))$/.exec(input);
|
|
18066
|
+
if (!match) return invalid();
|
|
18067
|
+
const year = Number(match[1]);
|
|
18068
|
+
const month = Number(match[2]);
|
|
18069
|
+
const day = Number(match[3]);
|
|
18070
|
+
const hour = Number(match[4]);
|
|
18071
|
+
const minute = Number(match[5]);
|
|
18072
|
+
const second = Number(match[6]);
|
|
18073
|
+
const fraction = match[7];
|
|
18074
|
+
if (month < 1 || month > 12) return invalid();
|
|
18075
|
+
const dim = daysInMonth(year, month);
|
|
18076
|
+
if (day < 1 || day > dim) return invalid("Day is out of range for month.");
|
|
18077
|
+
if (hour < 0 || hour > 23) return invalid("Hour must be between 00 and 23.");
|
|
18078
|
+
if (minute < 0 || minute > 59) return invalid("Minute must be between 00 and 59.");
|
|
18079
|
+
if (second < 0 || second > 59) return invalid("Second must be between 00 and 59.");
|
|
18080
|
+
const millis = fraction ? Number(fraction.padEnd(3, "0")) : 0;
|
|
18081
|
+
if (!Number.isInteger(millis) || millis < 0 || millis > 999) return invalid("Fractional seconds must have 1-3 digits.");
|
|
18082
|
+
const timezone = match[8];
|
|
18083
|
+
let offsetMinutes = 0;
|
|
18084
|
+
if (timezone !== "Z") {
|
|
18085
|
+
const sign = match[9];
|
|
18086
|
+
const offsetHours = Number(match[10]);
|
|
18087
|
+
const offsetMins = Number(match[11]);
|
|
18088
|
+
if (sign !== "+" && sign !== "-" || offsetHours < 0 || offsetHours > 23 || offsetMins < 0 || offsetMins > 59) return invalid("Timezone offset must be between -23:59 and +23:59.");
|
|
18089
|
+
offsetMinutes = (sign === "+" ? 1 : -1) * (offsetHours * 60 + offsetMins);
|
|
18090
|
+
}
|
|
18091
|
+
const utcMillis = Date.UTC(year, month - 1, day, hour, minute, second, millis) - offsetMinutes * 6e4;
|
|
18092
|
+
const date = new Date(utcMillis);
|
|
18093
|
+
if (Number.isNaN(date.getTime())) return invalid();
|
|
18094
|
+
return {
|
|
18095
|
+
ok: true,
|
|
18096
|
+
value: date
|
|
18097
|
+
};
|
|
18098
|
+
}
|
|
18099
|
+
|
|
18027
18100
|
//#endregion
|
|
18028
18101
|
//#region src/cli/commands/config.ts
|
|
18102
|
+
function parseSetupScriptSlug(value) {
|
|
18103
|
+
const slug = value.trim();
|
|
18104
|
+
if (!slug) throw new InvalidArgumentError("setup-script slug must be non-empty.");
|
|
18105
|
+
return slug;
|
|
18106
|
+
}
|
|
18029
18107
|
function createConfigAgentCommand(version) {
|
|
18030
|
-
return createCommand("config").version(version).description("Configure agent for pantheon project").argument("<name>", "The name of the agent.").argument("<role>", "The role of the agent.").
|
|
18031
|
-
const [name, role
|
|
18108
|
+
return createCommand("config").version(version).description("Configure agent for pantheon project").argument("<name>", "The name of the agent.").argument("<role>", "The role of the agent.").option("--project-id <project-id>", "The project id of the agent. Defaults to DEFAULT_PANTHEON_PROJECT_ID.").option("--skills <skills>", "The skills of the agent. Multiple values are separated by comma.", parseUniqueCommaList, []).option("--execute-agent <agent>", "The execute agent of the agent.", "codex").option("--root-branch-id <branchId>", "The root branch id of the agent. Defaults to DEFAULT_PANTHEON_ROOT_BRANCH_ID, then project root branch id.").option("--prototype-url <url>", "Role and skill definitions repo.", "https://github.com/pingcap-inc/pantheon-agents").option("--setup-script <slug>", "Setup script slug. CLI resolves it to setup-<slug>.sh.", parseSetupScriptSlug).action(async function() {
|
|
18109
|
+
const [name, role] = this.args;
|
|
18032
18110
|
const options = this.opts();
|
|
18111
|
+
const resolvedProjectId = resolvePantheonProjectId(options.projectId);
|
|
18112
|
+
if (!resolvedProjectId) {
|
|
18113
|
+
console.error("project-id is required (--project-id or DEFAULT_PANTHEON_PROJECT_ID).");
|
|
18114
|
+
process$1.exitCode = 1;
|
|
18115
|
+
return;
|
|
18116
|
+
}
|
|
18117
|
+
const resolvedRootBranchId = resolvePantheonRootBranchId(options.rootBranchId);
|
|
18033
18118
|
const requiredEnvVars = ["DATABASE_URL"];
|
|
18034
|
-
if (
|
|
18119
|
+
if (!resolvedRootBranchId) requiredEnvVars.push("PANTHEON_API_KEY");
|
|
18035
18120
|
if (!ensureEnv(requiredEnvVars)) return;
|
|
18036
18121
|
await configAgent(name, {
|
|
18037
18122
|
role,
|
|
18038
|
-
projectId,
|
|
18039
|
-
...options
|
|
18123
|
+
projectId: resolvedProjectId,
|
|
18124
|
+
...options,
|
|
18125
|
+
rootBranchId: resolvedRootBranchId,
|
|
18126
|
+
setupScript: options.setupScript ?? options.executeAgent
|
|
18040
18127
|
});
|
|
18041
18128
|
});
|
|
18042
18129
|
}
|
|
@@ -18156,82 +18243,18 @@ function createLlmExplainCommand(version) {
|
|
|
18156
18243
|
});
|
|
18157
18244
|
}
|
|
18158
18245
|
|
|
18159
|
-
//#endregion
|
|
18160
|
-
//#region src/cli/utils/parse.ts
|
|
18161
|
-
function parseCommaList(value) {
|
|
18162
|
-
return value.split(",").map((item) => item.trim()).filter((item) => item !== "");
|
|
18163
|
-
}
|
|
18164
|
-
function parseUniqueCommaList(value) {
|
|
18165
|
-
const items = parseCommaList(value);
|
|
18166
|
-
const seen = /* @__PURE__ */ new Set();
|
|
18167
|
-
const result = [];
|
|
18168
|
-
for (const item of items) {
|
|
18169
|
-
if (seen.has(item)) continue;
|
|
18170
|
-
seen.add(item);
|
|
18171
|
-
result.push(item);
|
|
18172
|
-
}
|
|
18173
|
-
return result;
|
|
18174
|
-
}
|
|
18175
|
-
function isLeapYear(year) {
|
|
18176
|
-
return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
|
|
18177
|
-
}
|
|
18178
|
-
function daysInMonth(year, month) {
|
|
18179
|
-
switch (month) {
|
|
18180
|
-
case 2: return isLeapYear(year) ? 29 : 28;
|
|
18181
|
-
case 4:
|
|
18182
|
-
case 6:
|
|
18183
|
-
case 9:
|
|
18184
|
-
case 11: return 30;
|
|
18185
|
-
default: return 31;
|
|
18186
|
-
}
|
|
18187
|
-
}
|
|
18188
|
-
function parseIsoTimestamp(value) {
|
|
18189
|
-
const invalid = (detail) => ({
|
|
18190
|
-
ok: false,
|
|
18191
|
-
error: `Invalid timestamp: ${JSON.stringify(value)}. ` + (detail ? `${detail} ` : "") + "Expected ISO-8601 date-time with timezone, e.g. 2026-02-12T00:00:00Z."
|
|
18192
|
-
});
|
|
18193
|
-
const input = value.trim();
|
|
18194
|
-
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?(Z|([+-])(\d{2}):(\d{2}))$/.exec(input);
|
|
18195
|
-
if (!match) return invalid();
|
|
18196
|
-
const year = Number(match[1]);
|
|
18197
|
-
const month = Number(match[2]);
|
|
18198
|
-
const day = Number(match[3]);
|
|
18199
|
-
const hour = Number(match[4]);
|
|
18200
|
-
const minute = Number(match[5]);
|
|
18201
|
-
const second = Number(match[6]);
|
|
18202
|
-
const fraction = match[7];
|
|
18203
|
-
if (month < 1 || month > 12) return invalid();
|
|
18204
|
-
const dim = daysInMonth(year, month);
|
|
18205
|
-
if (day < 1 || day > dim) return invalid("Day is out of range for month.");
|
|
18206
|
-
if (hour < 0 || hour > 23) return invalid("Hour must be between 00 and 23.");
|
|
18207
|
-
if (minute < 0 || minute > 59) return invalid("Minute must be between 00 and 59.");
|
|
18208
|
-
if (second < 0 || second > 59) return invalid("Second must be between 00 and 59.");
|
|
18209
|
-
const millis = fraction ? Number(fraction.padEnd(3, "0")) : 0;
|
|
18210
|
-
if (!Number.isInteger(millis) || millis < 0 || millis > 999) return invalid("Fractional seconds must have 1-3 digits.");
|
|
18211
|
-
const timezone = match[8];
|
|
18212
|
-
let offsetMinutes = 0;
|
|
18213
|
-
if (timezone !== "Z") {
|
|
18214
|
-
const sign = match[9];
|
|
18215
|
-
const offsetHours = Number(match[10]);
|
|
18216
|
-
const offsetMins = Number(match[11]);
|
|
18217
|
-
if (sign !== "+" && sign !== "-" || offsetHours < 0 || offsetHours > 23 || offsetMins < 0 || offsetMins > 59) return invalid("Timezone offset must be between -23:59 and +23:59.");
|
|
18218
|
-
offsetMinutes = (sign === "+" ? 1 : -1) * (offsetHours * 60 + offsetMins);
|
|
18219
|
-
}
|
|
18220
|
-
const utcMillis = Date.UTC(year, month - 1, day, hour, minute, second, millis) - offsetMinutes * 6e4;
|
|
18221
|
-
const date = new Date(utcMillis);
|
|
18222
|
-
if (Number.isNaN(date.getTime())) return invalid();
|
|
18223
|
-
return {
|
|
18224
|
-
ok: true,
|
|
18225
|
-
value: date
|
|
18226
|
-
};
|
|
18227
|
-
}
|
|
18228
|
-
|
|
18229
18246
|
//#endregion
|
|
18230
18247
|
//#region src/cli/commands/reconfig.ts
|
|
18231
18248
|
function createReconfigAgentCommand(version) {
|
|
18232
|
-
return createCommand("reconfig").version(version).description("Re-bootstrap an existing agent configuration for a project").argument("<name>", "The name of the agent.").
|
|
18233
|
-
const [name
|
|
18249
|
+
return createCommand("reconfig").version(version).description("Re-bootstrap an existing agent configuration for a project").argument("<name>", "The name of the agent.").option("--project-id <project-id>", "The project id of the agent. Defaults to DEFAULT_PANTHEON_PROJECT_ID.").option("--role <role>", "Override role for the agent.").option("--skills <skills>", "Override skills for the agent (comma-separated, replaces existing list).", parseUniqueCommaList).action(async function() {
|
|
18250
|
+
const [name] = this.args;
|
|
18234
18251
|
const options = this.opts();
|
|
18252
|
+
const projectId = resolvePantheonProjectId(options.projectId);
|
|
18253
|
+
if (!projectId) {
|
|
18254
|
+
console.error("project-id is required (--project-id or DEFAULT_PANTHEON_PROJECT_ID).");
|
|
18255
|
+
process$1.exitCode = 1;
|
|
18256
|
+
return;
|
|
18257
|
+
}
|
|
18235
18258
|
if (!ensureEnv(["DATABASE_URL", "PANTHEON_API_KEY"])) return;
|
|
18236
18259
|
const resolvedRole = options.role?.trim();
|
|
18237
18260
|
if (options.role != null && !resolvedRole) {
|
|
@@ -20843,6 +20866,7 @@ function agentConfigToDTO(config) {
|
|
|
20843
20866
|
role: config.role,
|
|
20844
20867
|
skills: config.skills,
|
|
20845
20868
|
prototype_url: config.prototype_url,
|
|
20869
|
+
setup_script: config.setup_script,
|
|
20846
20870
|
execute_agent: config.execute_agent
|
|
20847
20871
|
};
|
|
20848
20872
|
}
|
|
@@ -20856,67 +20880,16 @@ function assertPantheonApiKeyConfigured() {
|
|
|
20856
20880
|
message: "Missing PANTHEON_API_KEY. This endpoint requires Pantheon API access."
|
|
20857
20881
|
});
|
|
20858
20882
|
}
|
|
20859
|
-
async function
|
|
20860
|
-
let state;
|
|
20861
|
-
try {
|
|
20862
|
-
state = await options.getPantheonBranchFn({
|
|
20863
|
-
projectId: options.projectId,
|
|
20864
|
-
branchId: options.branchId,
|
|
20865
|
-
getOutputIfFinished: true,
|
|
20866
|
-
manifestingAsSucceed: true
|
|
20867
|
-
});
|
|
20868
|
-
} catch (error) {
|
|
20869
|
-
throw new ApiError({
|
|
20870
|
-
status: 502,
|
|
20871
|
-
code: "pantheon_api_error",
|
|
20872
|
-
message: `Failed to fetch Pantheon branch output for ${options.branchId}.`,
|
|
20873
|
-
details: {
|
|
20874
|
-
project_id: options.projectId,
|
|
20875
|
-
branch_id: options.branchId,
|
|
20876
|
-
error: getErrorMessage$2(error)
|
|
20877
|
-
}
|
|
20878
|
-
});
|
|
20879
|
-
}
|
|
20880
|
-
if (state.state === "succeed") return state.output ?? "";
|
|
20881
|
-
if (state.state === "failed") throw new ApiError({
|
|
20882
|
-
status: 502,
|
|
20883
|
-
code: "pantheon_branch_failed",
|
|
20884
|
-
message: `Pantheon branch ${options.branchId} failed.`,
|
|
20885
|
-
details: {
|
|
20886
|
-
project_id: options.projectId,
|
|
20887
|
-
branch_id: options.branchId,
|
|
20888
|
-
error: state.error
|
|
20889
|
-
}
|
|
20890
|
-
});
|
|
20891
|
-
throw new ApiError({
|
|
20892
|
-
status: 502,
|
|
20893
|
-
code: "pantheon_branch_not_ready",
|
|
20894
|
-
message: `Pantheon branch ${options.branchId} is not finished yet.`,
|
|
20895
|
-
details: {
|
|
20896
|
-
project_id: options.projectId,
|
|
20897
|
-
branch_id: options.branchId
|
|
20898
|
-
}
|
|
20899
|
-
});
|
|
20900
|
-
}
|
|
20901
|
-
async function getAgentConfigWithBootstrapOutput(options) {
|
|
20902
|
-
assertPantheonApiKeyConfigured();
|
|
20883
|
+
async function getAgentConfig(options) {
|
|
20903
20884
|
const config = await options.provider.getAgentConfig(options.projectId);
|
|
20904
20885
|
if (!config) throw new ApiError({
|
|
20905
20886
|
status: 404,
|
|
20906
20887
|
code: "config_not_found",
|
|
20907
20888
|
message: `No config found for agent ${options.agent} and project ${options.projectId}.`
|
|
20908
20889
|
});
|
|
20909
|
-
|
|
20910
|
-
projectId: config.project_id,
|
|
20911
|
-
branchId: config.base_branch_id,
|
|
20912
|
-
getPantheonBranchFn: options.getPantheonBranchFn ?? getPantheonBranch
|
|
20913
|
-
});
|
|
20914
|
-
return {
|
|
20915
|
-
config: agentConfigToDTO(config),
|
|
20916
|
-
bootstrap_output: bootstrapOutput
|
|
20917
|
-
};
|
|
20890
|
+
return { config: agentConfigToDTO(config) };
|
|
20918
20891
|
}
|
|
20919
|
-
async function
|
|
20892
|
+
async function reconfigAgentConfig(options) {
|
|
20920
20893
|
assertPantheonApiKeyConfigured();
|
|
20921
20894
|
const getPantheonBranchFn = options.getPantheonBranchFn ?? getPantheonBranch;
|
|
20922
20895
|
const wrappedGetPantheonBranch = (args) => getPantheonBranchFn({
|
|
@@ -20941,7 +20914,7 @@ async function reconfigAgentConfigWithBootstrapOutput(options) {
|
|
|
20941
20914
|
code: "config_not_found",
|
|
20942
20915
|
message
|
|
20943
20916
|
});
|
|
20944
|
-
if (message === "--role must be non-empty.") throw new ApiError({
|
|
20917
|
+
if (message === "--role must be non-empty." || message === "--setup-script must be non-empty.") throw new ApiError({
|
|
20945
20918
|
status: 400,
|
|
20946
20919
|
code: "validation_error",
|
|
20947
20920
|
message
|
|
@@ -20966,8 +20939,7 @@ async function reconfigAgentConfigWithBootstrapOutput(options) {
|
|
|
20966
20939
|
});
|
|
20967
20940
|
return {
|
|
20968
20941
|
previous_config: agentConfigToDTO(result.previousConfig),
|
|
20969
|
-
config: agentConfigToDTO(updated)
|
|
20970
|
-
bootstrap_output: result.bootstrapOutput
|
|
20942
|
+
config: agentConfigToDTO(updated)
|
|
20971
20943
|
};
|
|
20972
20944
|
}
|
|
20973
20945
|
|
|
@@ -21011,7 +20983,8 @@ const createTaskBodySchema = z$1.object({
|
|
|
21011
20983
|
});
|
|
21012
20984
|
const reconfigBodySchema = z$1.object({
|
|
21013
20985
|
role: z$1.string().trim().min(1).optional(),
|
|
21014
|
-
skills: z$1.array(z$1.string().trim().min(1)).optional()
|
|
20986
|
+
skills: z$1.array(z$1.string().trim().min(1)).optional(),
|
|
20987
|
+
setup_script: z$1.string().trim().min(1).optional()
|
|
21015
20988
|
});
|
|
21016
20989
|
function getAgentParam(value) {
|
|
21017
20990
|
if (typeof value !== "string" || value.trim().length === 0) throw new ApiError({
|
|
@@ -21046,7 +21019,7 @@ function createApiRouter(options) {
|
|
|
21046
21019
|
}));
|
|
21047
21020
|
router.get("/agents/:agent/configs/:project_id", asyncHandler(async (req, res) => {
|
|
21048
21021
|
const agent = getAgentParam(req.params.agent);
|
|
21049
|
-
const result = await
|
|
21022
|
+
const result = await getAgentConfig({
|
|
21050
21023
|
agent,
|
|
21051
21024
|
projectId: getProjectIdParam(req.params.project_id),
|
|
21052
21025
|
provider: new TaskListTidbProvider(agent, logger, { db })
|
|
@@ -21058,12 +21031,13 @@ function createApiRouter(options) {
|
|
|
21058
21031
|
const projectId = getProjectIdParam(req.params.project_id);
|
|
21059
21032
|
const body = reconfigBodySchema.parse(req.body);
|
|
21060
21033
|
const provider = new TaskListTidbProvider(agent, logger, { db });
|
|
21061
|
-
const result = await
|
|
21034
|
+
const result = await reconfigAgentConfig({
|
|
21062
21035
|
agent,
|
|
21063
21036
|
reconfig: {
|
|
21064
21037
|
projectId,
|
|
21065
21038
|
role: body.role,
|
|
21066
|
-
skills: body.skills
|
|
21039
|
+
skills: body.skills,
|
|
21040
|
+
setupScript: body.setup_script
|
|
21067
21041
|
},
|
|
21068
21042
|
provider
|
|
21069
21043
|
});
|
|
@@ -30958,7 +30932,7 @@ function createAgentsMcpServer(options) {
|
|
|
30958
30932
|
};
|
|
30959
30933
|
});
|
|
30960
30934
|
server.registerTool("configs.get", {
|
|
30961
|
-
description: "Get an agent's config for a project
|
|
30935
|
+
description: "Get an agent's config for a project.",
|
|
30962
30936
|
inputSchema: {
|
|
30963
30937
|
agent: z$1.string().min(1),
|
|
30964
30938
|
project_id: z$1.string().min(1)
|
|
@@ -30969,7 +30943,7 @@ function createAgentsMcpServer(options) {
|
|
|
30969
30943
|
type: "text",
|
|
30970
30944
|
text: "OK"
|
|
30971
30945
|
}],
|
|
30972
|
-
structuredContent: await
|
|
30946
|
+
structuredContent: await getAgentConfig({
|
|
30973
30947
|
agent,
|
|
30974
30948
|
projectId: project_id,
|
|
30975
30949
|
provider: new TaskListTidbProvider(agent, options.logger, { db: options.db })
|
|
@@ -30982,21 +30956,23 @@ function createAgentsMcpServer(options) {
|
|
|
30982
30956
|
agent: z$1.string().min(1),
|
|
30983
30957
|
project_id: z$1.string().min(1),
|
|
30984
30958
|
role: z$1.string().trim().min(1).optional(),
|
|
30985
|
-
skills: z$1.array(z$1.string().trim().min(1)).optional()
|
|
30959
|
+
skills: z$1.array(z$1.string().trim().min(1)).optional(),
|
|
30960
|
+
setup_script: z$1.string().trim().min(1).optional()
|
|
30986
30961
|
}
|
|
30987
|
-
}, async ({ agent, project_id, role, skills }) => {
|
|
30962
|
+
}, async ({ agent, project_id, role, skills, setup_script }) => {
|
|
30988
30963
|
const provider = new TaskListTidbProvider(agent, options.logger, { db: options.db });
|
|
30989
30964
|
return {
|
|
30990
30965
|
content: [{
|
|
30991
30966
|
type: "text",
|
|
30992
30967
|
text: "OK"
|
|
30993
30968
|
}],
|
|
30994
|
-
structuredContent: await
|
|
30969
|
+
structuredContent: await reconfigAgentConfig({
|
|
30995
30970
|
agent,
|
|
30996
30971
|
reconfig: {
|
|
30997
30972
|
projectId: project_id,
|
|
30998
30973
|
role,
|
|
30999
|
-
skills
|
|
30974
|
+
skills,
|
|
30975
|
+
setupScript: setup_script
|
|
31000
30976
|
},
|
|
31001
30977
|
provider
|
|
31002
30978
|
})
|
|
@@ -31121,9 +31097,10 @@ function createServerCommand(version) {
|
|
|
31121
31097
|
//#endregion
|
|
31122
31098
|
//#region src/cli/commands/show-config.ts
|
|
31123
31099
|
function createShowConfigCommand(version) {
|
|
31124
|
-
return createCommand("show-config").version(version).description("Show agent config for a project").argument("<name>", "The name of the agent.").
|
|
31125
|
-
const [name
|
|
31100
|
+
return createCommand("show-config").version(version).description("Show agent config for a project").argument("<name>", "The name of the agent.").option("--project-id <project-id>", "The project id. Defaults to DEFAULT_PANTHEON_PROJECT_ID.").option("--json", "Output config as JSON.").action(async function() {
|
|
31101
|
+
const [name] = this.args;
|
|
31126
31102
|
const options = this.opts();
|
|
31103
|
+
const projectId = resolvePantheonProjectId(options.projectId);
|
|
31127
31104
|
if (!ensureEnv(["DATABASE_URL"])) return;
|
|
31128
31105
|
if (projectId) {
|
|
31129
31106
|
const config = await showAgentConfig(name, projectId);
|
|
@@ -31143,6 +31120,7 @@ function createShowConfigCommand(version) {
|
|
|
31143
31120
|
role: config.role,
|
|
31144
31121
|
execute_agent: config.execute_agent,
|
|
31145
31122
|
prototype_url: config.prototype_url,
|
|
31123
|
+
setup_script: config.setup_script,
|
|
31146
31124
|
skills: Array.isArray(config.skills) ? config.skills.join(", ") : String(config.skills)
|
|
31147
31125
|
}]);
|
|
31148
31126
|
return;
|
|
@@ -31164,6 +31142,7 @@ function createShowConfigCommand(version) {
|
|
|
31164
31142
|
role: config.role,
|
|
31165
31143
|
execute_agent: config.execute_agent,
|
|
31166
31144
|
prototype_url: config.prototype_url,
|
|
31145
|
+
setup_script: config.setup_script,
|
|
31167
31146
|
skills: Array.isArray(config.skills) ? config.skills.join(", ") : String(config.skills)
|
|
31168
31147
|
})));
|
|
31169
31148
|
});
|
|
@@ -31370,8 +31349,8 @@ const mcpToolCallItemSchema = z$1.object({
|
|
|
31370
31349
|
result: z$1.object({
|
|
31371
31350
|
content: z$1.array(z$1.any()),
|
|
31372
31351
|
structured_content: z$1.any()
|
|
31373
|
-
}).optional(),
|
|
31374
|
-
error: z$1.object({ message: z$1.string() }).optional(),
|
|
31352
|
+
}).nullable().transform((v) => v ?? void 0).optional(),
|
|
31353
|
+
error: z$1.object({ message: z$1.string() }).nullable().transform((v) => v ?? void 0).optional(),
|
|
31375
31354
|
status: z$1.enum([
|
|
31376
31355
|
"in_progress",
|
|
31377
31356
|
"completed",
|
|
@@ -31760,16 +31739,40 @@ function createCodexNormalizer(options = {}) {
|
|
|
31760
31739
|
|
|
31761
31740
|
//#endregion
|
|
31762
31741
|
//#region ../agent-stream-parser/src/claude.ts
|
|
31763
|
-
|
|
31764
|
-
|
|
31765
|
-
|
|
31766
|
-
|
|
31767
|
-
|
|
31742
|
+
function toContentBlockEnvelope(value) {
|
|
31743
|
+
if (!isRecord(value) || typeof value.type !== "string") return null;
|
|
31744
|
+
return {
|
|
31745
|
+
type: value.type,
|
|
31746
|
+
text: typeof value.text === "string" ? value.text : void 0,
|
|
31747
|
+
id: typeof value.id === "string" ? value.id : void 0,
|
|
31748
|
+
name: typeof value.name === "string" ? value.name : void 0,
|
|
31749
|
+
input: value.input
|
|
31750
|
+
};
|
|
31751
|
+
}
|
|
31752
|
+
function toToolResultEnvelope(value) {
|
|
31753
|
+
if (!isRecord(value) || value.type !== "tool_result") return null;
|
|
31754
|
+
return {
|
|
31755
|
+
type: "tool_result",
|
|
31756
|
+
tool_use_id: typeof value.tool_use_id === "string" ? value.tool_use_id : void 0,
|
|
31757
|
+
content: value.content,
|
|
31758
|
+
is_error: typeof value.is_error === "boolean" ? value.is_error : void 0
|
|
31759
|
+
};
|
|
31760
|
+
}
|
|
31761
|
+
function getMessageContentBlocks(value) {
|
|
31762
|
+
if (!isRecord(value) || !Array.isArray(value.content)) return [];
|
|
31763
|
+
return value.content;
|
|
31764
|
+
}
|
|
31765
|
+
function isToolUseLikeType(type) {
|
|
31766
|
+
return type === "tool_use" || type === "mcp_tool_use" || type === "server_tool_use";
|
|
31767
|
+
}
|
|
31768
31768
|
function createClaudeCodeNormalizer(options = {}) {
|
|
31769
31769
|
const includeMetaEvents = options.includeMetaEvents ?? false;
|
|
31770
31770
|
const textBlocks = /* @__PURE__ */ new Map();
|
|
31771
31771
|
const toolBlocks = /* @__PURE__ */ new Map();
|
|
31772
|
-
|
|
31772
|
+
const assistantTextBlocks = /* @__PURE__ */ new Map();
|
|
31773
|
+
const knownToolCallIds = /* @__PURE__ */ new Set();
|
|
31774
|
+
let currentAssistantMessageId;
|
|
31775
|
+
let assistantFallbackMessageSeq = 0;
|
|
31773
31776
|
function maybeMeta(event) {
|
|
31774
31777
|
if (!includeMetaEvents) return [];
|
|
31775
31778
|
return [{
|
|
@@ -31777,145 +31780,50 @@ function createClaudeCodeNormalizer(options = {}) {
|
|
|
31777
31780
|
data: event
|
|
31778
31781
|
}];
|
|
31779
31782
|
}
|
|
31780
|
-
function
|
|
31781
|
-
const
|
|
31782
|
-
if (
|
|
31783
|
-
const id = existing?.id ?? `claude-text-${index}`;
|
|
31784
|
-
textBlocks.set(index, {
|
|
31785
|
-
id,
|
|
31786
|
-
open: true
|
|
31787
|
-
});
|
|
31788
|
-
return [{
|
|
31789
|
-
type: "text-start",
|
|
31790
|
-
id
|
|
31791
|
-
}];
|
|
31792
|
-
}
|
|
31793
|
-
function ensureToolBlock(index, block) {
|
|
31794
|
-
const existing = toolBlocks.get(index);
|
|
31795
|
-
if (existing?.open) return [];
|
|
31796
|
-
const toolCallId = block?.id ?? existing?.toolCallId ?? `claude-tool-${index}`;
|
|
31797
|
-
const toolName = block?.name ?? existing?.toolName ?? "tool";
|
|
31798
|
-
toolBlocks.set(index, {
|
|
31799
|
-
toolCallId,
|
|
31800
|
-
toolName,
|
|
31801
|
-
json: "",
|
|
31802
|
-
open: true
|
|
31803
|
-
});
|
|
31804
|
-
return [{
|
|
31805
|
-
type: "tool-input-start",
|
|
31806
|
-
toolCallId,
|
|
31807
|
-
toolName,
|
|
31808
|
-
dynamic: true
|
|
31809
|
-
}];
|
|
31810
|
-
}
|
|
31811
|
-
function handleStreamEvent(event) {
|
|
31812
|
-
if (!isRecord(event) || typeof event.type !== "string") return [];
|
|
31783
|
+
function handleAssistantMessage(message) {
|
|
31784
|
+
const resolvedMessageId = getAssistantMessageId(message);
|
|
31785
|
+
if (!resolvedMessageId) return [];
|
|
31813
31786
|
const out = [];
|
|
31814
|
-
|
|
31815
|
-
|
|
31816
|
-
|
|
31817
|
-
|
|
31818
|
-
|
|
31819
|
-
|
|
31820
|
-
|
|
31821
|
-
|
|
31822
|
-
|
|
31823
|
-
|
|
31824
|
-
|
|
31825
|
-
|
|
31826
|
-
|
|
31827
|
-
|
|
31828
|
-
|
|
31829
|
-
|
|
31830
|
-
|
|
31831
|
-
|
|
31832
|
-
|
|
31833
|
-
if (delta.type === "text_delta") {
|
|
31834
|
-
const text = typeof delta.text === "string" ? delta.text : "";
|
|
31835
|
-
if (!text) return [];
|
|
31836
|
-
out.push(...ensureTextBlock(index));
|
|
31837
|
-
const id = textBlocks.get(index).id;
|
|
31838
|
-
out.push({
|
|
31839
|
-
type: "text-delta",
|
|
31840
|
-
id,
|
|
31841
|
-
delta: text
|
|
31842
|
-
});
|
|
31843
|
-
return out;
|
|
31844
|
-
}
|
|
31845
|
-
if (delta.type === "input_json_delta") {
|
|
31846
|
-
const partialJson = typeof delta.partial_json === "string" ? delta.partial_json : "";
|
|
31847
|
-
if (!partialJson) return [];
|
|
31848
|
-
out.push(...ensureToolBlock(index));
|
|
31849
|
-
const tool = toolBlocks.get(index);
|
|
31850
|
-
tool.json += partialJson;
|
|
31851
|
-
out.push({
|
|
31852
|
-
type: "tool-input-delta",
|
|
31853
|
-
toolCallId: tool.toolCallId,
|
|
31854
|
-
inputTextDelta: partialJson
|
|
31855
|
-
});
|
|
31856
|
-
return out;
|
|
31857
|
-
}
|
|
31858
|
-
return [];
|
|
31859
|
-
}
|
|
31860
|
-
case "content_block_stop": {
|
|
31861
|
-
if (index == null) return [];
|
|
31862
|
-
sawStreamEvents = true;
|
|
31863
|
-
const text = textBlocks.get(index);
|
|
31864
|
-
if (text?.open) {
|
|
31865
|
-
text.open = false;
|
|
31787
|
+
if (currentAssistantMessageId && currentAssistantMessageId !== resolvedMessageId) out.push(...closeAssistantTextBlocksForMessage(currentAssistantMessageId));
|
|
31788
|
+
currentAssistantMessageId = resolvedMessageId;
|
|
31789
|
+
const blocks = getMessageContentBlocks(message);
|
|
31790
|
+
const seenTextBlockKeys = /* @__PURE__ */ new Set();
|
|
31791
|
+
blocks.forEach((block, index) => {
|
|
31792
|
+
const contentBlock = toContentBlockEnvelope(block);
|
|
31793
|
+
if (!contentBlock) return;
|
|
31794
|
+
if (contentBlock.type === "text") {
|
|
31795
|
+
const key = `${resolvedMessageId}:${index}`;
|
|
31796
|
+
seenTextBlockKeys.add(key);
|
|
31797
|
+
let state = assistantTextBlocks.get(key);
|
|
31798
|
+
if (!state || !state.open) {
|
|
31799
|
+
state = {
|
|
31800
|
+
id: state?.id ?? `claude-assistant-text-${resolvedMessageId}-${index}`,
|
|
31801
|
+
messageId: resolvedMessageId,
|
|
31802
|
+
lastText: state?.lastText ?? "",
|
|
31803
|
+
open: true
|
|
31804
|
+
};
|
|
31805
|
+
assistantTextBlocks.set(key, state);
|
|
31866
31806
|
out.push({
|
|
31867
|
-
type: "text-
|
|
31868
|
-
id:
|
|
31807
|
+
type: "text-start",
|
|
31808
|
+
id: state.id
|
|
31869
31809
|
});
|
|
31870
31810
|
}
|
|
31871
|
-
const
|
|
31872
|
-
|
|
31873
|
-
|
|
31874
|
-
let input = tool.json ? tool.json : {};
|
|
31875
|
-
if (typeof input === "string") {
|
|
31876
|
-
const parsed = safeJsonParse(input);
|
|
31877
|
-
if (parsed.ok) input = parsed.value;
|
|
31878
|
-
}
|
|
31811
|
+
const fullText = contentBlock.text ?? "";
|
|
31812
|
+
const { delta, reset } = computeAppendDelta(state.lastText, fullText);
|
|
31813
|
+
if (delta) {
|
|
31879
31814
|
out.push({
|
|
31880
|
-
type: "
|
|
31881
|
-
|
|
31882
|
-
|
|
31883
|
-
input,
|
|
31884
|
-
dynamic: true
|
|
31815
|
+
type: "text-delta",
|
|
31816
|
+
id: state.id,
|
|
31817
|
+
delta
|
|
31885
31818
|
});
|
|
31819
|
+
state.lastText = reset ? fullText : state.lastText + delta;
|
|
31886
31820
|
}
|
|
31887
|
-
return out;
|
|
31888
|
-
}
|
|
31889
|
-
default: return [];
|
|
31890
|
-
}
|
|
31891
|
-
}
|
|
31892
|
-
function handleAssistantMessage(message) {
|
|
31893
|
-
if (sawStreamEvents) return [];
|
|
31894
|
-
const blocks = Array.isArray(message.content) ? message.content : [];
|
|
31895
|
-
const out = [];
|
|
31896
|
-
blocks.forEach((block, index) => {
|
|
31897
|
-
if (!isRecord(block) || typeof block.type !== "string") return;
|
|
31898
|
-
if (block.type === "text") {
|
|
31899
|
-
const id = `claude-assistant-text-${index}`;
|
|
31900
|
-
const text = typeof block.text === "string" ? block.text : "";
|
|
31901
|
-
out.push({
|
|
31902
|
-
type: "text-start",
|
|
31903
|
-
id
|
|
31904
|
-
});
|
|
31905
|
-
if (text) out.push({
|
|
31906
|
-
type: "text-delta",
|
|
31907
|
-
id,
|
|
31908
|
-
delta: text
|
|
31909
|
-
});
|
|
31910
|
-
out.push({
|
|
31911
|
-
type: "text-end",
|
|
31912
|
-
id
|
|
31913
|
-
});
|
|
31914
31821
|
}
|
|
31915
|
-
if (
|
|
31916
|
-
const toolCallId =
|
|
31917
|
-
const toolName =
|
|
31918
|
-
const input =
|
|
31822
|
+
if (isToolUseLikeType(contentBlock.type)) {
|
|
31823
|
+
const toolCallId = contentBlock.id ?? `claude-assistant-tool-${index}`;
|
|
31824
|
+
const toolName = contentBlock.name ?? "tool";
|
|
31825
|
+
const input = isRecord(contentBlock.input) ? contentBlock.input : contentBlock.input;
|
|
31826
|
+
knownToolCallIds.add(toolCallId);
|
|
31919
31827
|
out.push({
|
|
31920
31828
|
type: "tool-input-available",
|
|
31921
31829
|
toolCallId,
|
|
@@ -31925,6 +31833,33 @@ function createClaudeCodeNormalizer(options = {}) {
|
|
|
31925
31833
|
});
|
|
31926
31834
|
}
|
|
31927
31835
|
});
|
|
31836
|
+
for (const [key, state] of assistantTextBlocks) {
|
|
31837
|
+
if (state.messageId !== resolvedMessageId) continue;
|
|
31838
|
+
if (!state.open) continue;
|
|
31839
|
+
if (seenTextBlockKeys.has(key)) continue;
|
|
31840
|
+
state.open = false;
|
|
31841
|
+
out.push({
|
|
31842
|
+
type: "text-end",
|
|
31843
|
+
id: state.id
|
|
31844
|
+
});
|
|
31845
|
+
}
|
|
31846
|
+
return out;
|
|
31847
|
+
}
|
|
31848
|
+
function getAssistantMessageId(message) {
|
|
31849
|
+
if (isRecord(message) && typeof message.id === "string") return message.id;
|
|
31850
|
+
assistantFallbackMessageSeq += 1;
|
|
31851
|
+
return `claude-assistant-fallback-${assistantFallbackMessageSeq}`;
|
|
31852
|
+
}
|
|
31853
|
+
function closeAssistantTextBlocksForMessage(messageId) {
|
|
31854
|
+
const out = [];
|
|
31855
|
+
for (const state of assistantTextBlocks.values()) {
|
|
31856
|
+
if (state.messageId !== messageId || !state.open) continue;
|
|
31857
|
+
state.open = false;
|
|
31858
|
+
out.push({
|
|
31859
|
+
type: "text-end",
|
|
31860
|
+
id: state.id
|
|
31861
|
+
});
|
|
31862
|
+
}
|
|
31928
31863
|
return out;
|
|
31929
31864
|
}
|
|
31930
31865
|
function formatToolResultOutput(content, toolUseResult) {
|
|
@@ -31948,14 +31883,26 @@ function createClaudeCodeNormalizer(options = {}) {
|
|
|
31948
31883
|
return "Tool execution failed";
|
|
31949
31884
|
}
|
|
31950
31885
|
function handleUserMessage(message, toolUseResult) {
|
|
31951
|
-
const blocks =
|
|
31886
|
+
const blocks = getMessageContentBlocks(message);
|
|
31952
31887
|
const out = [];
|
|
31953
31888
|
blocks.forEach((block, index) => {
|
|
31954
|
-
|
|
31955
|
-
|
|
31956
|
-
const
|
|
31889
|
+
const toolResult = toToolResultEnvelope(block);
|
|
31890
|
+
if (!toolResult) return;
|
|
31891
|
+
const toolCallId = toolResult.tool_use_id ?? `claude-tool-result-${index}`;
|
|
31892
|
+
const content = toolResult.content;
|
|
31957
31893
|
const interrupted = isRecord(toolUseResult) && toolUseResult.interrupted === true;
|
|
31958
|
-
|
|
31894
|
+
const isError = toolResult.is_error === true || interrupted;
|
|
31895
|
+
if (!knownToolCallIds.has(toolCallId)) {
|
|
31896
|
+
knownToolCallIds.add(toolCallId);
|
|
31897
|
+
out.push({
|
|
31898
|
+
type: "tool-input-available",
|
|
31899
|
+
toolCallId,
|
|
31900
|
+
toolName: "tool",
|
|
31901
|
+
input: {},
|
|
31902
|
+
dynamic: true
|
|
31903
|
+
});
|
|
31904
|
+
}
|
|
31905
|
+
if (isError) {
|
|
31959
31906
|
out.push({
|
|
31960
31907
|
type: "tool-output-error",
|
|
31961
31908
|
toolCallId,
|
|
@@ -31974,12 +31921,11 @@ function createClaudeCodeNormalizer(options = {}) {
|
|
|
31974
31921
|
return out;
|
|
31975
31922
|
}
|
|
31976
31923
|
function push(chunk) {
|
|
31977
|
-
|
|
31978
|
-
if (!parsed.success) return {
|
|
31924
|
+
if (!isRecord(chunk) || typeof chunk.type !== "string") return {
|
|
31979
31925
|
recognized: false,
|
|
31980
31926
|
chunks: []
|
|
31981
31927
|
};
|
|
31982
|
-
const msg =
|
|
31928
|
+
const msg = chunk;
|
|
31983
31929
|
const out = [];
|
|
31984
31930
|
switch (msg.type) {
|
|
31985
31931
|
case "system": {
|
|
@@ -32003,39 +31949,28 @@ function createClaudeCodeNormalizer(options = {}) {
|
|
|
32003
31949
|
chunks: out
|
|
32004
31950
|
};
|
|
32005
31951
|
}
|
|
32006
|
-
case "stream_event": {
|
|
32007
|
-
|
|
32008
|
-
|
|
32009
|
-
|
|
32010
|
-
|
|
32011
|
-
|
|
32012
|
-
return {
|
|
32013
|
-
recognized: true,
|
|
32014
|
-
chunks: handleStreamEvent(event.data)
|
|
32015
|
-
};
|
|
32016
|
-
}
|
|
32017
|
-
case "assistant": {
|
|
32018
|
-
const message = msg.message;
|
|
32019
|
-
if (!isRecord(message)) return {
|
|
31952
|
+
case "stream_event": return {
|
|
31953
|
+
recognized: true,
|
|
31954
|
+
chunks: []
|
|
31955
|
+
};
|
|
31956
|
+
case "assistant":
|
|
31957
|
+
if (!isRecord(msg.message)) return {
|
|
32020
31958
|
recognized: false,
|
|
32021
31959
|
chunks: []
|
|
32022
31960
|
};
|
|
32023
31961
|
return {
|
|
32024
31962
|
recognized: true,
|
|
32025
|
-
chunks: handleAssistantMessage(message)
|
|
31963
|
+
chunks: handleAssistantMessage(msg.message)
|
|
32026
31964
|
};
|
|
32027
|
-
|
|
32028
|
-
|
|
32029
|
-
const message = msg.message;
|
|
32030
|
-
if (!isRecord(message)) return {
|
|
31965
|
+
case "user":
|
|
31966
|
+
if (!isRecord(msg.message)) return {
|
|
32031
31967
|
recognized: false,
|
|
32032
31968
|
chunks: []
|
|
32033
31969
|
};
|
|
32034
31970
|
return {
|
|
32035
31971
|
recognized: true,
|
|
32036
|
-
chunks: handleUserMessage(message, msg.tool_use_result)
|
|
31972
|
+
chunks: handleUserMessage(msg.message, msg.tool_use_result)
|
|
32037
31973
|
};
|
|
32038
|
-
}
|
|
32039
31974
|
case "result": return {
|
|
32040
31975
|
recognized: true,
|
|
32041
31976
|
chunks: maybeMeta({
|
|
@@ -32071,6 +32006,14 @@ function createClaudeCodeNormalizer(options = {}) {
|
|
|
32071
32006
|
dynamic: true
|
|
32072
32007
|
});
|
|
32073
32008
|
}
|
|
32009
|
+
for (const state of assistantTextBlocks.values()) {
|
|
32010
|
+
if (!state.open) continue;
|
|
32011
|
+
state.open = false;
|
|
32012
|
+
out.push({
|
|
32013
|
+
type: "text-end",
|
|
32014
|
+
id: state.id
|
|
32015
|
+
});
|
|
32016
|
+
}
|
|
32074
32017
|
return out;
|
|
32075
32018
|
}
|
|
32076
32019
|
return {
|
|
@@ -32198,7 +32141,7 @@ function commandBasename(value) {
|
|
|
32198
32141
|
const lastSlash = value.lastIndexOf("/");
|
|
32199
32142
|
return lastSlash === -1 ? value : value.slice(lastSlash + 1);
|
|
32200
32143
|
}
|
|
32201
|
-
function parseShellEntries(line) {
|
|
32144
|
+
function parseShellEntries$1(line) {
|
|
32202
32145
|
try {
|
|
32203
32146
|
return parse(line);
|
|
32204
32147
|
} catch {
|
|
@@ -32241,7 +32184,7 @@ function pickLastNonOptionArg(tokens) {
|
|
|
32241
32184
|
return values.length > 0 ? values[values.length - 1] : void 0;
|
|
32242
32185
|
}
|
|
32243
32186
|
function parseExploredItem(line) {
|
|
32244
|
-
const entries = parseShellEntries(normalizeCommand(line));
|
|
32187
|
+
const entries = parseShellEntries$1(normalizeCommand(line));
|
|
32245
32188
|
if (!entries) return void 0;
|
|
32246
32189
|
const tokens = takeMainTokens(entries);
|
|
32247
32190
|
if (tokens.length === 0) return void 0;
|
|
@@ -32320,128 +32263,8 @@ function formatCommandExecutionDisplayCommand(command) {
|
|
|
32320
32263
|
}
|
|
32321
32264
|
|
|
32322
32265
|
//#endregion
|
|
32323
|
-
//#region src/cli/commands/watch.ts
|
|
32324
|
-
const
|
|
32325
|
-
function parseListWidth(value) {
|
|
32326
|
-
const width = Number(value);
|
|
32327
|
-
if (!Number.isInteger(width) || width <= 0) throw new InvalidArgumentError("list-width requires a positive integer, e.g. 32");
|
|
32328
|
-
return width;
|
|
32329
|
-
}
|
|
32330
|
-
function ensureEnvOrExit(keys) {
|
|
32331
|
-
const missing = keys.filter((key) => !process.env[key]);
|
|
32332
|
-
if (missing.length === 0) return;
|
|
32333
|
-
for (const key of missing) console.error(`${key} environment variable is not set.`);
|
|
32334
|
-
process.exit(1);
|
|
32335
|
-
}
|
|
32336
|
-
function toStreamSourceFromExecuteAgent(executeAgent) {
|
|
32337
|
-
if (!executeAgent) return void 0;
|
|
32338
|
-
const normalized = executeAgent.toLowerCase();
|
|
32339
|
-
if (normalized.includes("claude")) return "claude-code";
|
|
32340
|
-
if (normalized.includes("codex")) return "codex";
|
|
32341
|
-
}
|
|
32342
|
-
function detectStreamSourceFromRawPayload(payload) {
|
|
32343
|
-
if (payload.includes("\"type\":\"thread.") || payload.includes("\"type\":\"turn.") || payload.includes("\"type\":\"item.")) return "codex";
|
|
32344
|
-
if (payload.includes("\"type\":\"message_start\"") || payload.includes("\"type\":\"message_delta\"") || payload.includes("\"type\":\"message_stop\"") || payload.includes("\"type\":\"content_block_start\"") || payload.includes("\"type\":\"content_block_delta\"") || payload.includes("\"type\":\"content_block_stop\"")) return "claude-code";
|
|
32345
|
-
}
|
|
32346
|
-
async function getAgentExecuteAgentMap(db, targets) {
|
|
32347
|
-
const agents = Array.from(new Set(targets.map((t) => t.agent)));
|
|
32348
|
-
if (agents.length === 0) return /* @__PURE__ */ new Map();
|
|
32349
|
-
const configs = await db.selectFrom("agent_project_config").select([
|
|
32350
|
-
"agent",
|
|
32351
|
-
"project_id",
|
|
32352
|
-
"execute_agent"
|
|
32353
|
-
]).where("agent", "in", agents).execute();
|
|
32354
|
-
const map = /* @__PURE__ */ new Map();
|
|
32355
|
-
for (const row of configs) map.set(`${row.agent}|${row.project_id}`, row.execute_agent);
|
|
32356
|
-
return map;
|
|
32357
|
-
}
|
|
32358
|
-
async function loadTasksByIds(db, ids) {
|
|
32359
|
-
if (ids.length === 0) return [];
|
|
32360
|
-
const rows = await db.selectFrom("task").selectAll().where("id", "in", ids).execute();
|
|
32361
|
-
const out = [];
|
|
32362
|
-
for (const row of rows) {
|
|
32363
|
-
const parsed = taskItemSchema.safeParse(row);
|
|
32364
|
-
if (!parsed.success) {
|
|
32365
|
-
console.error(`Failed to parse task row id=${row.id} agent=${row.agent} status=${row.status}: ${parsed.error.message}`);
|
|
32366
|
-
continue;
|
|
32367
|
-
}
|
|
32368
|
-
out.push({
|
|
32369
|
-
agent: row.agent,
|
|
32370
|
-
task: parsed.data
|
|
32371
|
-
});
|
|
32372
|
-
}
|
|
32373
|
-
return out;
|
|
32374
|
-
}
|
|
32375
|
-
async function loadTasksForAgent(db, agent) {
|
|
32376
|
-
const rows = await db.selectFrom("task").selectAll().where("agent", "=", agent).execute();
|
|
32377
|
-
const out = [];
|
|
32378
|
-
for (const row of rows) {
|
|
32379
|
-
const parsed = taskItemSchema.safeParse(row);
|
|
32380
|
-
if (!parsed.success) continue;
|
|
32381
|
-
out.push(parsed.data);
|
|
32382
|
-
}
|
|
32383
|
-
return out;
|
|
32384
|
-
}
|
|
32385
|
-
async function loadAllTasks(db, limit) {
|
|
32386
|
-
const rows = await db.selectFrom("task").selectAll().orderBy("queued_at", "desc").limit(limit).execute();
|
|
32387
|
-
const out = [];
|
|
32388
|
-
for (const row of rows) {
|
|
32389
|
-
const parsed = taskItemSchema.safeParse(row);
|
|
32390
|
-
if (!parsed.success) continue;
|
|
32391
|
-
out.push({
|
|
32392
|
-
agent: row.agent,
|
|
32393
|
-
task: parsed.data
|
|
32394
|
-
});
|
|
32395
|
-
}
|
|
32396
|
-
return out;
|
|
32397
|
-
}
|
|
32398
|
-
async function resolveWatchTargets(options) {
|
|
32399
|
-
const warnings = [];
|
|
32400
|
-
let selected = [];
|
|
32401
|
-
if (options.taskIds.length > 0) {
|
|
32402
|
-
const uniqueTaskIds = Array.from(new Set(options.taskIds));
|
|
32403
|
-
const found = await loadTasksByIds(options.db, uniqueTaskIds);
|
|
32404
|
-
const foundIds = new Set(found.map((t) => t.task.id));
|
|
32405
|
-
const missing = uniqueTaskIds.filter((id) => !foundIds.has(id));
|
|
32406
|
-
if (missing.length > 0) warnings.push(`Requested task IDs not found: ${missing.join(", ")}`);
|
|
32407
|
-
const byId = new Map(found.map((t) => [t.task.id, t]));
|
|
32408
|
-
selected = uniqueTaskIds.map((id) => byId.get(id)).filter(Boolean);
|
|
32409
|
-
if (selected.length === 0) warnings.push("No requested tasks found; falling back to default selection.");
|
|
32410
|
-
} else if (options.agentNames.length > 0) {
|
|
32411
|
-
const uniqueAgents = Array.from(new Set(options.agentNames));
|
|
32412
|
-
if (uniqueAgents.length === 1) {
|
|
32413
|
-
selected = selectSingleAgentWatchTasks(await loadTasksForAgent(options.db, uniqueAgents[0]), { finishedLimit: 3 }).map((task) => ({
|
|
32414
|
-
agent: uniqueAgents[0],
|
|
32415
|
-
task
|
|
32416
|
-
}));
|
|
32417
|
-
if (selected.length === 0) warnings.push(`No tasks found for agent ${uniqueAgents[0]}; falling back to default selection.`);
|
|
32418
|
-
} else {
|
|
32419
|
-
selected = selectMultiAgentWatchTasks(await Promise.all(uniqueAgents.map(async (agent) => {
|
|
32420
|
-
return (await loadTasksForAgent(options.db, agent)).map((task) => ({
|
|
32421
|
-
agent,
|
|
32422
|
-
task
|
|
32423
|
-
}));
|
|
32424
|
-
})).then((groups) => groups.flat()), uniqueAgents);
|
|
32425
|
-
const foundAgents = new Set(selected.map((t) => t.agent));
|
|
32426
|
-
const missing = uniqueAgents.filter((agent) => !foundAgents.has(agent));
|
|
32427
|
-
if (missing.length > 0) warnings.push(`No tasks found for agent(s): ${missing.join(", ")}`);
|
|
32428
|
-
if (selected.length === 0) warnings.push("No requested agent tasks found; falling back to default selection.");
|
|
32429
|
-
}
|
|
32430
|
-
} else warnings.push("No targets specified; selecting up to 3 agents automatically.");
|
|
32431
|
-
if (selected.length === 0) selected = selectFallbackWatchTasks(await loadAllTasks(options.db, 500), { agentLimit: 3 });
|
|
32432
|
-
const executeAgentMap = await getAgentExecuteAgentMap(options.db, selected);
|
|
32433
|
-
return {
|
|
32434
|
-
targets: selected.map((entry) => ({
|
|
32435
|
-
...entry,
|
|
32436
|
-
executeAgent: executeAgentMap.get(`${entry.agent}|${entry.task.project_id}`)
|
|
32437
|
-
})),
|
|
32438
|
-
warnings
|
|
32439
|
-
};
|
|
32440
|
-
}
|
|
32441
|
-
function makeStreamUrl(streamId) {
|
|
32442
|
-
return `${PANTHEON_BASE_URL}/ai_stream_proxy/v2/streams/${encodeURIComponent(streamId)}/stream?format=opaque-stream-json`;
|
|
32443
|
-
}
|
|
32444
|
-
const ANSI = {
|
|
32266
|
+
//#region src/cli/commands/watch/log-output.ts
|
|
32267
|
+
const ANSI$1 = {
|
|
32445
32268
|
reset: "\x1B[0m",
|
|
32446
32269
|
bold: "\x1B[1m",
|
|
32447
32270
|
dim: "\x1B[2m",
|
|
@@ -32451,67 +32274,280 @@ const ANSI = {
|
|
|
32451
32274
|
cyan: "\x1B[36m",
|
|
32452
32275
|
gray: "\x1B[90m"
|
|
32453
32276
|
};
|
|
32454
|
-
function
|
|
32455
|
-
|
|
32456
|
-
if (process.env.NO_COLOR != null) return false;
|
|
32457
|
-
return true;
|
|
32277
|
+
function sgrWrap$1(text, code, enabled) {
|
|
32278
|
+
return enabled ? `${code}${text}${ANSI$1.reset}` : text;
|
|
32458
32279
|
}
|
|
32459
|
-
function
|
|
32460
|
-
return
|
|
32461
|
-
}
|
|
32462
|
-
function bold(text, enabled) {
|
|
32463
|
-
return sgrWrap(text, ANSI.bold, enabled);
|
|
32280
|
+
function bold$1(text, enabled) {
|
|
32281
|
+
return sgrWrap$1(text, ANSI$1.bold, enabled);
|
|
32464
32282
|
}
|
|
32465
32283
|
function dim(text, enabled) {
|
|
32466
|
-
return sgrWrap(text, ANSI.dim, enabled);
|
|
32284
|
+
return sgrWrap$1(text, ANSI$1.dim, enabled);
|
|
32467
32285
|
}
|
|
32468
32286
|
function gray(text, enabled) {
|
|
32469
|
-
return sgrWrap(text, ANSI.gray, enabled);
|
|
32287
|
+
return sgrWrap$1(text, ANSI$1.gray, enabled);
|
|
32470
32288
|
}
|
|
32471
32289
|
function red(text, enabled) {
|
|
32472
|
-
return sgrWrap(text, ANSI.red, enabled);
|
|
32290
|
+
return sgrWrap$1(text, ANSI$1.red, enabled);
|
|
32473
32291
|
}
|
|
32474
32292
|
function green(text, enabled) {
|
|
32475
|
-
return sgrWrap(text, ANSI.green, enabled);
|
|
32293
|
+
return sgrWrap$1(text, ANSI$1.green, enabled);
|
|
32476
32294
|
}
|
|
32477
32295
|
function yellow(text, enabled) {
|
|
32478
|
-
return sgrWrap(text, ANSI.yellow, enabled);
|
|
32296
|
+
return sgrWrap$1(text, ANSI$1.yellow, enabled);
|
|
32479
32297
|
}
|
|
32480
32298
|
function cyan(text, enabled) {
|
|
32481
|
-
return sgrWrap(text, ANSI.cyan, enabled);
|
|
32299
|
+
return sgrWrap$1(text, ANSI$1.cyan, enabled);
|
|
32482
32300
|
}
|
|
32483
|
-
function dimGray(text, enabled) {
|
|
32484
|
-
return sgrWrap(text, `${ANSI.dim}${ANSI.gray}`, enabled);
|
|
32301
|
+
function dimGray$1(text, enabled) {
|
|
32302
|
+
return sgrWrap$1(text, `${ANSI$1.dim}${ANSI$1.gray}`, enabled);
|
|
32485
32303
|
}
|
|
32486
32304
|
function kv(label, value, style) {
|
|
32487
32305
|
return `${dim(`${label}:`, style)} ${value}`;
|
|
32488
32306
|
}
|
|
32307
|
+
function getCommandExecutionRawCommand(tool) {
|
|
32308
|
+
const title = typeof tool.title === "string" ? tool.title : "";
|
|
32309
|
+
const inputCommand = typeof tool.input?.command === "string" ? tool.input.command : "";
|
|
32310
|
+
return title || inputCommand || void 0;
|
|
32311
|
+
}
|
|
32489
32312
|
function getCommandExecutionDisplayCommand(tool) {
|
|
32490
32313
|
const raw = getCommandExecutionRawCommand(tool);
|
|
32491
32314
|
if (!raw) return void 0;
|
|
32492
32315
|
return formatCommandExecutionDisplayCommand(raw) || void 0;
|
|
32493
32316
|
}
|
|
32494
|
-
function
|
|
32495
|
-
|
|
32496
|
-
|
|
32497
|
-
|
|
32498
|
-
|
|
32317
|
+
function isCommandLikeTool(toolName) {
|
|
32318
|
+
if (!toolName) return false;
|
|
32319
|
+
return toolName === "command_execution" || toolName.toLowerCase() === "bash";
|
|
32320
|
+
}
|
|
32321
|
+
function isClaudeExplorationTool(toolName) {
|
|
32322
|
+
if (!toolName) return false;
|
|
32323
|
+
const normalized = toolName.toLowerCase();
|
|
32324
|
+
return normalized === "read" || normalized === "glob" || normalized === "grep";
|
|
32325
|
+
}
|
|
32326
|
+
function isWriteTool(toolName) {
|
|
32327
|
+
if (!toolName) return false;
|
|
32328
|
+
return toolName.toLowerCase() === "write";
|
|
32329
|
+
}
|
|
32330
|
+
function isEditTool(toolName) {
|
|
32331
|
+
if (!toolName) return false;
|
|
32332
|
+
return toolName.toLowerCase() === "edit";
|
|
32333
|
+
}
|
|
32334
|
+
function toRecord(value) {
|
|
32335
|
+
return typeof value === "object" && value !== null ? value : void 0;
|
|
32336
|
+
}
|
|
32337
|
+
function toStringValue(value) {
|
|
32338
|
+
return typeof value === "string" ? value : void 0;
|
|
32339
|
+
}
|
|
32340
|
+
function toStringOrUndefined(value) {
|
|
32341
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
32342
|
+
}
|
|
32343
|
+
function extractWritePath(input) {
|
|
32344
|
+
const record = toRecord(input);
|
|
32345
|
+
if (!record) return void 0;
|
|
32346
|
+
return toStringOrUndefined(record.file_path) ?? toStringOrUndefined(record.path) ?? toStringOrUndefined(record.filePath) ?? toStringOrUndefined(record.filepath) ?? toStringOrUndefined(record.filename);
|
|
32347
|
+
}
|
|
32348
|
+
function countContentLines(content) {
|
|
32349
|
+
const normalized = content.replaceAll("\r\n", "\n");
|
|
32350
|
+
if (!normalized) return 0;
|
|
32351
|
+
const lines = normalized.split("\n");
|
|
32352
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
32353
|
+
return lines.length;
|
|
32354
|
+
}
|
|
32355
|
+
function extractWriteAddedLineCount(input) {
|
|
32356
|
+
const record = toRecord(input);
|
|
32357
|
+
if (!record) return void 0;
|
|
32358
|
+
const content = toStringValue(record.content);
|
|
32359
|
+
if (content == null) return void 0;
|
|
32360
|
+
return countContentLines(content);
|
|
32361
|
+
}
|
|
32362
|
+
function extractEditAddedLineCount(input) {
|
|
32363
|
+
const record = toRecord(input);
|
|
32364
|
+
if (!record) return void 0;
|
|
32365
|
+
const oldText = toStringValue(record.old_string) ?? toStringValue(record.oldString) ?? "";
|
|
32366
|
+
const newText = toStringValue(record.new_string) ?? toStringValue(record.newString);
|
|
32367
|
+
if (newText == null) return void 0;
|
|
32368
|
+
const oldLines = countContentLines(oldText);
|
|
32369
|
+
const newLines = countContentLines(newText);
|
|
32370
|
+
return Math.max(0, newLines - oldLines);
|
|
32371
|
+
}
|
|
32372
|
+
function formatWriteToolLabel(tool, style) {
|
|
32373
|
+
const renderedName = bold$1(tool.toolName ?? "Write", style);
|
|
32374
|
+
const path = extractWritePath(tool.input);
|
|
32375
|
+
const addedLines = extractWriteAddedLineCount(tool.input);
|
|
32376
|
+
const chunks = [renderedName];
|
|
32377
|
+
if (path) chunks.push(path);
|
|
32378
|
+
if (addedLines != null) {
|
|
32379
|
+
const unit = addedLines === 1 ? "line" : "lines";
|
|
32380
|
+
chunks.push(`(${green(`+${addedLines} ${unit}`, style)})`);
|
|
32381
|
+
}
|
|
32382
|
+
if (!path && addedLines == null && tool.title) chunks.push(`: ${tool.title}`);
|
|
32383
|
+
return chunks.join(" ");
|
|
32384
|
+
}
|
|
32385
|
+
function formatEditToolLabel(tool, style) {
|
|
32386
|
+
const renderedName = bold$1(tool.toolName ?? "Edit", style);
|
|
32387
|
+
const path = extractWritePath(tool.input);
|
|
32388
|
+
const addedLines = extractEditAddedLineCount(tool.input);
|
|
32389
|
+
const chunks = [renderedName];
|
|
32390
|
+
if (path) chunks.push(path);
|
|
32391
|
+
if (addedLines != null) {
|
|
32392
|
+
const unit = addedLines === 1 ? "line" : "lines";
|
|
32393
|
+
chunks.push(`(${green(`+${addedLines} ${unit}`, style)})`);
|
|
32394
|
+
}
|
|
32395
|
+
if (!path && addedLines == null && tool.title) chunks.push(`: ${tool.title}`);
|
|
32396
|
+
return chunks.join(" ");
|
|
32397
|
+
}
|
|
32398
|
+
function stripOuterQuotes(token) {
|
|
32399
|
+
if (token.length < 2) return token;
|
|
32400
|
+
const first = token[0];
|
|
32401
|
+
const last = token[token.length - 1];
|
|
32402
|
+
if (first === "'" && last === "'" || first === "\"" && last === "\"") return token.slice(1, -1);
|
|
32403
|
+
return token;
|
|
32499
32404
|
}
|
|
32500
|
-
function
|
|
32501
|
-
const
|
|
32502
|
-
const
|
|
32503
|
-
|
|
32405
|
+
function isEnvAssignmentToken(token) {
|
|
32406
|
+
const normalized = stripOuterQuotes(token);
|
|
32407
|
+
const equalsAt = normalized.indexOf("=");
|
|
32408
|
+
if (equalsAt <= 0) return false;
|
|
32409
|
+
const key = normalized.slice(0, equalsAt);
|
|
32410
|
+
return /^[A-Za-z_][A-Za-z0-9_]*$/.test(key);
|
|
32411
|
+
}
|
|
32412
|
+
function isExecPrefixToken(token) {
|
|
32413
|
+
const normalized = stripOuterQuotes(token);
|
|
32414
|
+
return normalized === "env" || normalized === "sudo" || normalized === "builtin" || normalized === "time" || normalized === "nohup";
|
|
32415
|
+
}
|
|
32416
|
+
function parseShellEntries(line) {
|
|
32417
|
+
try {
|
|
32418
|
+
return parse(line);
|
|
32419
|
+
} catch {
|
|
32420
|
+
return;
|
|
32421
|
+
}
|
|
32422
|
+
}
|
|
32423
|
+
function colorizeExecToken(token, style) {
|
|
32424
|
+
if (!token) return token;
|
|
32425
|
+
const core = token;
|
|
32426
|
+
if (!core) return token;
|
|
32427
|
+
const lastSlash = core.lastIndexOf("/");
|
|
32428
|
+
if (lastSlash === -1 || lastSlash === core.length - 1) return bold$1(core, style);
|
|
32429
|
+
return `${core.slice(0, lastSlash + 1)}${bold$1(core.slice(lastSlash + 1), style)}`;
|
|
32430
|
+
}
|
|
32431
|
+
function isShellOperatorEntry(entry) {
|
|
32432
|
+
return typeof entry === "object" && entry != null && "op" in entry && typeof entry.op === "string";
|
|
32433
|
+
}
|
|
32434
|
+
function isShellCommentEntry(entry) {
|
|
32435
|
+
return typeof entry === "object" && entry != null && "comment" in entry && typeof entry.comment === "string";
|
|
32436
|
+
}
|
|
32437
|
+
function renderShellToken(rawToken, styledToken) {
|
|
32438
|
+
if (rawToken.length === 0) return `""`;
|
|
32439
|
+
if (!/\s/.test(rawToken)) return styledToken;
|
|
32440
|
+
return `"${styledToken.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"")}"`;
|
|
32441
|
+
}
|
|
32442
|
+
function findCommandSubstitutionEnd(text, openAt) {
|
|
32443
|
+
let depth = 1;
|
|
32444
|
+
let i = openAt;
|
|
32445
|
+
let escaped = false;
|
|
32446
|
+
while (i < text.length) {
|
|
32447
|
+
const ch = text[i] ?? "";
|
|
32448
|
+
if (escaped) {
|
|
32449
|
+
escaped = false;
|
|
32450
|
+
i += 1;
|
|
32451
|
+
continue;
|
|
32452
|
+
}
|
|
32453
|
+
if (ch === "\\") {
|
|
32454
|
+
escaped = true;
|
|
32455
|
+
i += 1;
|
|
32456
|
+
continue;
|
|
32457
|
+
}
|
|
32458
|
+
if (ch === "$" && text[i + 1] === "(") {
|
|
32459
|
+
depth += 1;
|
|
32460
|
+
i += 2;
|
|
32461
|
+
continue;
|
|
32462
|
+
}
|
|
32463
|
+
if (ch === ")") {
|
|
32464
|
+
depth -= 1;
|
|
32465
|
+
if (depth === 0) return i;
|
|
32466
|
+
}
|
|
32467
|
+
i += 1;
|
|
32468
|
+
}
|
|
32469
|
+
return -1;
|
|
32470
|
+
}
|
|
32471
|
+
function colorizeCommandSubstitutions(token, style) {
|
|
32472
|
+
if (!token.includes("$(")) return token;
|
|
32473
|
+
let out = "";
|
|
32474
|
+
let i = 0;
|
|
32475
|
+
let cursor = 0;
|
|
32476
|
+
while (i < token.length) {
|
|
32477
|
+
if (token[i] === "$" && token[i + 1] === "(") {
|
|
32478
|
+
out += token.slice(cursor, i);
|
|
32479
|
+
const closeAt = findCommandSubstitutionEnd(token, i + 2);
|
|
32480
|
+
if (closeAt < 0) {
|
|
32481
|
+
out += token.slice(i);
|
|
32482
|
+
return out;
|
|
32483
|
+
}
|
|
32484
|
+
const innerColorized = colorizeCommandLine(token.slice(i + 2, closeAt), style);
|
|
32485
|
+
out += `$(${innerColorized})`;
|
|
32486
|
+
i = closeAt + 1;
|
|
32487
|
+
cursor = i;
|
|
32488
|
+
continue;
|
|
32489
|
+
}
|
|
32490
|
+
i += 1;
|
|
32491
|
+
}
|
|
32492
|
+
out += token.slice(cursor);
|
|
32493
|
+
return out;
|
|
32494
|
+
}
|
|
32495
|
+
const SEGMENT_BREAK_OPERATORS = new Set([
|
|
32496
|
+
"|",
|
|
32497
|
+
"|&",
|
|
32498
|
+
"||",
|
|
32499
|
+
"&&",
|
|
32500
|
+
";",
|
|
32501
|
+
"&"
|
|
32502
|
+
]);
|
|
32503
|
+
const GRAY_CONNECTORS = new Set([
|
|
32504
|
+
"|",
|
|
32505
|
+
"|&",
|
|
32506
|
+
"||",
|
|
32507
|
+
"&&"
|
|
32508
|
+
]);
|
|
32509
|
+
function colorizeCommandLine(line, style) {
|
|
32510
|
+
if (!line) return line;
|
|
32511
|
+
const entries = parseShellEntries(line);
|
|
32512
|
+
if (!entries || entries.length === 0) return line;
|
|
32513
|
+
const rendered = [];
|
|
32514
|
+
let shouldHighlightExec = true;
|
|
32515
|
+
for (const entry of entries) {
|
|
32516
|
+
if (typeof entry === "string") {
|
|
32517
|
+
const token = entry;
|
|
32518
|
+
const isExecToken = shouldHighlightExec && !isEnvAssignmentToken(token) && !isExecPrefixToken(token);
|
|
32519
|
+
let styledToken = token;
|
|
32520
|
+
if (isExecToken) {
|
|
32521
|
+
styledToken = colorizeExecToken(token, style);
|
|
32522
|
+
shouldHighlightExec = false;
|
|
32523
|
+
} else styledToken = colorizeCommandSubstitutions(token, style);
|
|
32524
|
+
rendered.push(renderShellToken(token, styledToken));
|
|
32525
|
+
continue;
|
|
32526
|
+
}
|
|
32527
|
+
if (isShellCommentEntry(entry)) {
|
|
32528
|
+
rendered.push(`#${entry.comment}`);
|
|
32529
|
+
break;
|
|
32530
|
+
}
|
|
32531
|
+
if (!isShellOperatorEntry(entry)) continue;
|
|
32532
|
+
rendered.push(GRAY_CONNECTORS.has(entry.op) ? gray(entry.op, style) : entry.op);
|
|
32533
|
+
if (SEGMENT_BREAK_OPERATORS.has(entry.op)) shouldHighlightExec = true;
|
|
32534
|
+
}
|
|
32535
|
+
return rendered.join(" ");
|
|
32536
|
+
}
|
|
32537
|
+
function colorizeCommandProgram(display, style) {
|
|
32538
|
+
return display.replaceAll("\r\n", "\n").split("\n").map((line) => colorizeCommandLine(line, style)).join("\n");
|
|
32504
32539
|
}
|
|
32505
32540
|
function formatToolLabel(tool, style) {
|
|
32506
32541
|
const name = tool.toolName ?? "<tool>";
|
|
32507
|
-
if (
|
|
32542
|
+
if (isWriteTool(tool.toolName)) return formatWriteToolLabel(tool, style);
|
|
32543
|
+
if (isEditTool(tool.toolName)) return formatEditToolLabel(tool, style);
|
|
32544
|
+
if (isCommandLikeTool(tool.toolName)) {
|
|
32508
32545
|
const rawCommand = getCommandExecutionRawCommand(tool);
|
|
32509
32546
|
const exploredLines = rawCommand ? getExploredCommandLines(rawCommand) : void 0;
|
|
32510
32547
|
if (exploredLines && exploredLines.length > 1) return cyan(exploredLines[0] ?? "Explored", style);
|
|
32511
|
-
|
|
32512
|
-
return `${bold("$", style)} ${colorizeCommandProgram(display, style)}`;
|
|
32548
|
+
return colorizeCommandProgram(getCommandExecutionDisplayCommand(tool) ?? "(unknown command)", style);
|
|
32513
32549
|
}
|
|
32514
|
-
const renderedName = bold(name, style);
|
|
32550
|
+
const renderedName = bold$1(name, style);
|
|
32515
32551
|
if (!tool.title) return renderedName;
|
|
32516
32552
|
return `${renderedName}: ${tool.title}`;
|
|
32517
32553
|
}
|
|
@@ -32542,12 +32578,37 @@ function formatToolStatusIcon(status, style) {
|
|
|
32542
32578
|
}
|
|
32543
32579
|
}
|
|
32544
32580
|
function getExploredSubCommandLines(tool) {
|
|
32545
|
-
if (tool.toolName
|
|
32546
|
-
|
|
32547
|
-
|
|
32548
|
-
|
|
32549
|
-
|
|
32550
|
-
|
|
32581
|
+
if (isCommandLikeTool(tool.toolName)) {
|
|
32582
|
+
const rawCommand = getCommandExecutionRawCommand(tool);
|
|
32583
|
+
if (!rawCommand) return void 0;
|
|
32584
|
+
const exploredLines = getExploredCommandLines(rawCommand);
|
|
32585
|
+
if (!exploredLines || exploredLines.length <= 1) return void 0;
|
|
32586
|
+
return exploredLines.slice(1);
|
|
32587
|
+
}
|
|
32588
|
+
if (!isClaudeExplorationTool(tool.toolName)) return void 0;
|
|
32589
|
+
const input = toRecord(tool.input) ?? {};
|
|
32590
|
+
const toolName = (tool.toolName ?? "").toLowerCase();
|
|
32591
|
+
if (toolName === "read") {
|
|
32592
|
+
const path = toStringOrUndefined(input.file_path) ?? toStringOrUndefined(input.path) ?? toStringOrUndefined(input.filePath);
|
|
32593
|
+
if (!path) return ["Read"];
|
|
32594
|
+
return [`Read ${path}`];
|
|
32595
|
+
}
|
|
32596
|
+
if (toolName === "glob") {
|
|
32597
|
+
const pattern = toStringOrUndefined(input.pattern);
|
|
32598
|
+
const path = toStringOrUndefined(input.path);
|
|
32599
|
+
if (path && pattern) return [`List ${path} (glob: ${pattern})`];
|
|
32600
|
+
if (path) return [`List ${path}`];
|
|
32601
|
+
if (pattern) return [`List (glob: ${pattern})`];
|
|
32602
|
+
return ["List"];
|
|
32603
|
+
}
|
|
32604
|
+
if (toolName === "grep") {
|
|
32605
|
+
const pattern = toStringOrUndefined(input.pattern);
|
|
32606
|
+
const path = toStringOrUndefined(input.path);
|
|
32607
|
+
if (pattern && path) return [`Search ${pattern} in ${path}`];
|
|
32608
|
+
if (pattern) return [`Search ${pattern}`];
|
|
32609
|
+
if (path) return [`Search in ${path}`];
|
|
32610
|
+
return ["Search"];
|
|
32611
|
+
}
|
|
32551
32612
|
}
|
|
32552
32613
|
function formatExploredSubCommandLine(line, style) {
|
|
32553
32614
|
if (line.startsWith("Search ")) {
|
|
@@ -32556,7 +32617,7 @@ function formatExploredSubCommandLine(line, style) {
|
|
|
32556
32617
|
if (at !== -1) {
|
|
32557
32618
|
const terms = payload.slice(0, at);
|
|
32558
32619
|
const file = payload.slice(at + 4);
|
|
32559
|
-
return `${cyan("Search", style)} ${terms} ${dimGray("in", style)} ${file}`;
|
|
32620
|
+
return `${cyan("Search", style)} ${terms} ${dimGray$1("in", style)} ${file}`;
|
|
32560
32621
|
}
|
|
32561
32622
|
return `${cyan("Search", style)} ${payload}`;
|
|
32562
32623
|
}
|
|
@@ -32568,7 +32629,7 @@ function formatExploredSubCommandLine(line, style) {
|
|
|
32568
32629
|
function toDisplayLines(text) {
|
|
32569
32630
|
return text.replaceAll("\r\n", "\n").split("\n");
|
|
32570
32631
|
}
|
|
32571
|
-
function wrapRawLineToWidth(line, width) {
|
|
32632
|
+
function wrapRawLineToWidth$1(line, width) {
|
|
32572
32633
|
if (width <= 0) return [line];
|
|
32573
32634
|
if (line.length === 0) return [""];
|
|
32574
32635
|
const out = [];
|
|
@@ -32595,21 +32656,33 @@ function trimCommandOutputTopBottom(lines) {
|
|
|
32595
32656
|
lines[lines.length - 1]
|
|
32596
32657
|
];
|
|
32597
32658
|
}
|
|
32659
|
+
function getCommandLikeOutputText(output) {
|
|
32660
|
+
const value = output;
|
|
32661
|
+
const aggregated = typeof value?.aggregated_output === "string" ? value.aggregated_output : "";
|
|
32662
|
+
if (aggregated) return aggregated;
|
|
32663
|
+
const nestedToolResult = value?.tool_use_result;
|
|
32664
|
+
const stdout = typeof nestedToolResult?.stdout === "string" ? nestedToolResult.stdout : typeof value?.stdout === "string" ? value.stdout : "";
|
|
32665
|
+
const stderr = typeof nestedToolResult?.stderr === "string" ? nestedToolResult.stderr : typeof value?.stderr === "string" ? value.stderr : "";
|
|
32666
|
+
if (stdout || stderr) {
|
|
32667
|
+
if (stdout && stderr) return `${stdout}${stdout.endsWith("\n") ? "" : "\n"}${stderr}`;
|
|
32668
|
+
return stdout || stderr;
|
|
32669
|
+
}
|
|
32670
|
+
if (typeof value?.content === "string") return value.content;
|
|
32671
|
+
}
|
|
32598
32672
|
function formatToolResultPreviewLines(tool, options) {
|
|
32599
32673
|
if (tool.status === "in_progress") return [];
|
|
32600
32674
|
if (options.maxLines <= 0) return [];
|
|
32601
|
-
if (tool.toolName
|
|
32675
|
+
if (isCommandLikeTool(tool.toolName)) {
|
|
32602
32676
|
const rawCommand = getCommandExecutionRawCommand(tool);
|
|
32603
32677
|
if (rawCommand && isExploredCommandExecution(rawCommand)) return [];
|
|
32604
32678
|
}
|
|
32605
32679
|
if (tool.status === "failed") {
|
|
32606
32680
|
const lines = toDisplayLines(tool.errorText ?? "(no error text)");
|
|
32607
|
-
return trimLinesWithEllipsis(tool.toolName
|
|
32681
|
+
return trimLinesWithEllipsis(isCommandLikeTool(tool.toolName) ? trimCommandOutputTopBottom(lines) : lines, options.maxLines);
|
|
32608
32682
|
}
|
|
32609
32683
|
if (tool.output == null) return ["(none)"];
|
|
32610
|
-
if (tool.toolName
|
|
32611
|
-
const
|
|
32612
|
-
const allLines = toDisplayLines((typeof output?.aggregated_output === "string" ? output.aggregated_output : "").trimEnd());
|
|
32684
|
+
if (isCommandLikeTool(tool.toolName)) {
|
|
32685
|
+
const allLines = toDisplayLines((getCommandLikeOutputText(tool.output) ?? "").trimEnd());
|
|
32613
32686
|
if (!allLines.some((line) => line.length > 0)) return ["(none)"];
|
|
32614
32687
|
return trimLinesWithEllipsis(trimCommandOutputTopBottom(allLines), options.maxLines);
|
|
32615
32688
|
}
|
|
@@ -32621,7 +32694,124 @@ function formatToolResultPreviewLines(tool, options) {
|
|
|
32621
32694
|
breakLength: Math.max(20, options.widthHint)
|
|
32622
32695
|
})), options.maxLines);
|
|
32623
32696
|
}
|
|
32697
|
+
function formatWatchLogLines(options) {
|
|
32698
|
+
const blocks = [];
|
|
32699
|
+
const reasoningWrapWidth = Math.max(1, options.widthHint);
|
|
32700
|
+
const messageWrapWidth = Math.max(1, options.widthHint);
|
|
32701
|
+
const toolById = new Map(options.snapshot.toolActions.map((tool) => [tool.toolCallId, tool]));
|
|
32702
|
+
function pushBlock(order, lines) {
|
|
32703
|
+
if (lines.length === 0) return;
|
|
32704
|
+
blocks.push({
|
|
32705
|
+
order: order ?? Number.MAX_SAFE_INTEGER,
|
|
32706
|
+
lines
|
|
32707
|
+
});
|
|
32708
|
+
}
|
|
32709
|
+
for (let timelineIndex = 0; timelineIndex < options.snapshot.timeline.length; timelineIndex++) {
|
|
32710
|
+
const entry = options.snapshot.timeline[timelineIndex];
|
|
32711
|
+
if (entry.kind === "reasoning") {
|
|
32712
|
+
const reasoningBlock = [];
|
|
32713
|
+
const hasTrailingLineFeed = entry.text.endsWith("\n");
|
|
32714
|
+
const rawReasoningLines = toDisplayLines(entry.text);
|
|
32715
|
+
for (const rawLine of rawReasoningLines) wrapRawLineToWidth$1(rawLine, reasoningWrapWidth).forEach((chunk) => reasoningBlock.push(gray(chunk, options.style)));
|
|
32716
|
+
if (!hasTrailingLineFeed) reasoningBlock.push("");
|
|
32717
|
+
pushBlock(entry.order, reasoningBlock);
|
|
32718
|
+
continue;
|
|
32719
|
+
}
|
|
32720
|
+
if (entry.kind === "message") {
|
|
32721
|
+
const messageBlock = [];
|
|
32722
|
+
const rawMessageLines = toDisplayLines(entry.text);
|
|
32723
|
+
for (const rawLine of rawMessageLines) wrapRawLineToWidth$1(rawLine, messageWrapWidth).forEach((chunk) => messageBlock.push(chunk));
|
|
32724
|
+
pushBlock(entry.order, messageBlock);
|
|
32725
|
+
continue;
|
|
32726
|
+
}
|
|
32727
|
+
if (entry.kind === "warning") {
|
|
32728
|
+
pushBlock(entry.order, [kv("warning", yellow(entry.message, options.style), options.style)]);
|
|
32729
|
+
continue;
|
|
32730
|
+
}
|
|
32731
|
+
const tool = toolById.get(entry.toolCallId);
|
|
32732
|
+
if (!tool) continue;
|
|
32733
|
+
const exploredSubLines = getExploredSubCommandLines(tool);
|
|
32734
|
+
if (exploredSubLines != null) {
|
|
32735
|
+
const groupedTools = [tool];
|
|
32736
|
+
const groupedSubLines = [...exploredSubLines];
|
|
32737
|
+
let lookahead = timelineIndex + 1;
|
|
32738
|
+
while (lookahead < options.snapshot.timeline.length) {
|
|
32739
|
+
const next = options.snapshot.timeline[lookahead];
|
|
32740
|
+
if (next.kind !== "tool") break;
|
|
32741
|
+
const nextTool = toolById.get(next.toolCallId);
|
|
32742
|
+
if (!nextTool) break;
|
|
32743
|
+
const nextSubLines = getExploredSubCommandLines(nextTool);
|
|
32744
|
+
if (nextSubLines == null) break;
|
|
32745
|
+
groupedTools.push(nextTool);
|
|
32746
|
+
groupedSubLines.push(...nextSubLines);
|
|
32747
|
+
lookahead += 1;
|
|
32748
|
+
}
|
|
32749
|
+
const groupedBlock = [` ${[
|
|
32750
|
+
formatToolStatusIcon(combineToolStatuses(groupedTools.map((groupedTool) => groupedTool.status)), options.style),
|
|
32751
|
+
cyan("Explored", options.style),
|
|
32752
|
+
groupedTools.length > 1 ? gray(`x${groupedTools.length}`, options.style) : void 0
|
|
32753
|
+
].filter(Boolean).join(" ")}`];
|
|
32754
|
+
groupedSubLines.forEach((subLine, subIndex) => groupedBlock.push(subIndex === 0 ? ` ${dimGray$1("└", options.style)} ${formatExploredSubCommandLine(subLine, options.style)}` : ` ${formatExploredSubCommandLine(subLine, options.style)}`));
|
|
32755
|
+
pushBlock(entry.order, groupedBlock);
|
|
32756
|
+
timelineIndex = lookahead - 1;
|
|
32757
|
+
continue;
|
|
32758
|
+
}
|
|
32759
|
+
const toolBlock = [];
|
|
32760
|
+
const summaryLines = toDisplayLines(formatToolSummaryLine(tool, options.style));
|
|
32761
|
+
const summaryContinuationIndent = isCommandLikeTool(tool.toolName) ? " " : " ";
|
|
32762
|
+
summaryLines.forEach((line, index) => toolBlock.push(index === 0 ? ` ${line}` : `${summaryContinuationIndent}${line}`));
|
|
32763
|
+
if (isCommandLikeTool(tool.toolName)) formatToolResultPreviewLines(tool, {
|
|
32764
|
+
maxLines: Number.POSITIVE_INFINITY,
|
|
32765
|
+
widthHint: Math.max(20, options.widthHint - 10)
|
|
32766
|
+
}).forEach((line, index) => toolBlock.push(index === 0 ? ` ${dimGray$1("└", options.style)} ${dimGray$1(line, options.style)}` : ` ${dimGray$1(line, options.style)}`));
|
|
32767
|
+
pushBlock(entry.order, toolBlock);
|
|
32768
|
+
}
|
|
32769
|
+
if (options.lastError) pushBlock(void 0, [kv("error", red(options.lastError, options.style), options.style)]);
|
|
32770
|
+
blocks.sort((a, b) => a.order - b.order);
|
|
32771
|
+
const lines = [];
|
|
32772
|
+
for (const block of blocks) {
|
|
32773
|
+
if (lines.length > 0 && lines[lines.length - 1] !== "") lines.push("");
|
|
32774
|
+
lines.push(...block.lines);
|
|
32775
|
+
}
|
|
32776
|
+
if (lines.length === 0) lines.push(gray("(waiting for formatted stream output)", options.style));
|
|
32777
|
+
return lines;
|
|
32778
|
+
}
|
|
32779
|
+
|
|
32780
|
+
//#endregion
|
|
32781
|
+
//#region src/cli/commands/watch/source.ts
|
|
32782
|
+
function toStreamSourceFromExecuteAgent(executeAgent) {
|
|
32783
|
+
if (!executeAgent) return void 0;
|
|
32784
|
+
const normalized = executeAgent.toLowerCase();
|
|
32785
|
+
if (normalized.includes("claude")) return "claude-code";
|
|
32786
|
+
if (normalized.includes("codex")) return "codex";
|
|
32787
|
+
}
|
|
32788
|
+
function detectStreamSourceFromRawPayload(payload) {
|
|
32789
|
+
if (payload.includes("\"type\":\"thread.") || payload.includes("\"type\":\"turn.") || payload.includes("\"type\":\"item.")) return "codex";
|
|
32790
|
+
if (payload.includes("\"type\":\"message_start\"") || payload.includes("\"type\":\"message_delta\"") || payload.includes("\"type\":\"message_stop\"") || payload.includes("\"type\":\"content_block_start\"") || payload.includes("\"type\":\"content_block_delta\"") || payload.includes("\"type\":\"content_block_stop\"")) return "claude-code";
|
|
32791
|
+
}
|
|
32792
|
+
function parseAgentStreamSourceOption(value) {
|
|
32793
|
+
const normalized = value.trim().toLowerCase();
|
|
32794
|
+
if (normalized === "codex") return "codex";
|
|
32795
|
+
if (normalized === "claude-code" || normalized === "claude") return "claude-code";
|
|
32796
|
+
throw new Error(`Invalid stream source: ${value}. Allowed: codex, claude-code`);
|
|
32797
|
+
}
|
|
32798
|
+
|
|
32799
|
+
//#endregion
|
|
32800
|
+
//#region src/cli/commands/watch/stream-url.ts
|
|
32801
|
+
const PANTHEON_BASE_URL = "https://pantheon-ai.tidb.ai";
|
|
32802
|
+
function makeOpaqueStreamUrl(streamId) {
|
|
32803
|
+
return `${PANTHEON_BASE_URL}/ai_stream_proxy/v2/streams/${encodeURIComponent(streamId)}/stream?format=opaque-stream-json`;
|
|
32804
|
+
}
|
|
32805
|
+
|
|
32806
|
+
//#endregion
|
|
32807
|
+
//#region src/cli/commands/watch/tui.ts
|
|
32808
|
+
const ANSI_RESET = "\x1B[0m";
|
|
32624
32809
|
const ANSI_SGR_REGEX = /\u001b\[[0-9;]*m/g;
|
|
32810
|
+
function useAnsiStyles() {
|
|
32811
|
+
if (!process$1.stdout.isTTY) return false;
|
|
32812
|
+
if (process$1.env.NO_COLOR != null) return false;
|
|
32813
|
+
return true;
|
|
32814
|
+
}
|
|
32625
32815
|
function visibleLength(value) {
|
|
32626
32816
|
return value.replaceAll(ANSI_SGR_REGEX, "").length;
|
|
32627
32817
|
}
|
|
@@ -32649,7 +32839,142 @@ function clip(value, maxVisible) {
|
|
|
32649
32839
|
if (visibleLength(value) <= maxVisible) return value;
|
|
32650
32840
|
if (maxVisible === 1) return "…";
|
|
32651
32841
|
const sliced = sliceSgrByVisibleWidth(value, maxVisible - 1);
|
|
32652
|
-
return `${sliced}…${sliced.includes("\x1B[") ?
|
|
32842
|
+
return `${sliced}…${sliced.includes("\x1B[") ? ANSI_RESET : ""}`;
|
|
32843
|
+
}
|
|
32844
|
+
|
|
32845
|
+
//#endregion
|
|
32846
|
+
//#region src/cli/commands/watch.ts
|
|
32847
|
+
function parseListWidth(value) {
|
|
32848
|
+
const width = Number(value);
|
|
32849
|
+
if (!Number.isInteger(width) || width <= 0) throw new InvalidArgumentError("list-width requires a positive integer, e.g. 32");
|
|
32850
|
+
return width;
|
|
32851
|
+
}
|
|
32852
|
+
function ensureEnvOrExit(keys) {
|
|
32853
|
+
const missing = keys.filter((key) => !process.env[key]);
|
|
32854
|
+
if (missing.length === 0) return;
|
|
32855
|
+
for (const key of missing) console.error(`${key} environment variable is not set.`);
|
|
32856
|
+
process.exit(1);
|
|
32857
|
+
}
|
|
32858
|
+
async function getAgentExecuteAgentMap(db, targets) {
|
|
32859
|
+
const agents = Array.from(new Set(targets.map((t) => t.agent)));
|
|
32860
|
+
if (agents.length === 0) return /* @__PURE__ */ new Map();
|
|
32861
|
+
const configs = await db.selectFrom("agent_project_config").select([
|
|
32862
|
+
"agent",
|
|
32863
|
+
"project_id",
|
|
32864
|
+
"execute_agent"
|
|
32865
|
+
]).where("agent", "in", agents).execute();
|
|
32866
|
+
const map = /* @__PURE__ */ new Map();
|
|
32867
|
+
for (const row of configs) map.set(`${row.agent}|${row.project_id}`, row.execute_agent);
|
|
32868
|
+
return map;
|
|
32869
|
+
}
|
|
32870
|
+
async function loadTasksByIds(db, ids) {
|
|
32871
|
+
if (ids.length === 0) return [];
|
|
32872
|
+
const rows = await db.selectFrom("task").selectAll().where("id", "in", ids).execute();
|
|
32873
|
+
const out = [];
|
|
32874
|
+
for (const row of rows) {
|
|
32875
|
+
const parsed = taskItemSchema.safeParse(row);
|
|
32876
|
+
if (!parsed.success) {
|
|
32877
|
+
console.error(`Failed to parse task row id=${row.id} agent=${row.agent} status=${row.status}: ${parsed.error.message}`);
|
|
32878
|
+
continue;
|
|
32879
|
+
}
|
|
32880
|
+
out.push({
|
|
32881
|
+
agent: row.agent,
|
|
32882
|
+
task: parsed.data
|
|
32883
|
+
});
|
|
32884
|
+
}
|
|
32885
|
+
return out;
|
|
32886
|
+
}
|
|
32887
|
+
async function loadTasksForAgent(db, agent) {
|
|
32888
|
+
const rows = await db.selectFrom("task").selectAll().where("agent", "=", agent).execute();
|
|
32889
|
+
const out = [];
|
|
32890
|
+
for (const row of rows) {
|
|
32891
|
+
const parsed = taskItemSchema.safeParse(row);
|
|
32892
|
+
if (!parsed.success) continue;
|
|
32893
|
+
out.push(parsed.data);
|
|
32894
|
+
}
|
|
32895
|
+
return out;
|
|
32896
|
+
}
|
|
32897
|
+
async function loadAllTasks(db, limit) {
|
|
32898
|
+
const rows = await db.selectFrom("task").selectAll().orderBy("queued_at", "desc").limit(limit).execute();
|
|
32899
|
+
const out = [];
|
|
32900
|
+
for (const row of rows) {
|
|
32901
|
+
const parsed = taskItemSchema.safeParse(row);
|
|
32902
|
+
if (!parsed.success) continue;
|
|
32903
|
+
out.push({
|
|
32904
|
+
agent: row.agent,
|
|
32905
|
+
task: parsed.data
|
|
32906
|
+
});
|
|
32907
|
+
}
|
|
32908
|
+
return out;
|
|
32909
|
+
}
|
|
32910
|
+
async function resolveWatchTargets(options) {
|
|
32911
|
+
const warnings = [];
|
|
32912
|
+
let selected = [];
|
|
32913
|
+
if (options.taskIds.length > 0) {
|
|
32914
|
+
const uniqueTaskIds = Array.from(new Set(options.taskIds));
|
|
32915
|
+
const found = await loadTasksByIds(options.db, uniqueTaskIds);
|
|
32916
|
+
const foundIds = new Set(found.map((t) => t.task.id));
|
|
32917
|
+
const missing = uniqueTaskIds.filter((id) => !foundIds.has(id));
|
|
32918
|
+
if (missing.length > 0) warnings.push(`Requested task IDs not found: ${missing.join(", ")}`);
|
|
32919
|
+
const byId = new Map(found.map((t) => [t.task.id, t]));
|
|
32920
|
+
selected = uniqueTaskIds.map((id) => byId.get(id)).filter(Boolean);
|
|
32921
|
+
if (selected.length === 0) warnings.push("No requested tasks found; falling back to default selection.");
|
|
32922
|
+
} else if (options.agentNames.length > 0) {
|
|
32923
|
+
const uniqueAgents = Array.from(new Set(options.agentNames));
|
|
32924
|
+
if (uniqueAgents.length === 1) {
|
|
32925
|
+
selected = selectSingleAgentWatchTasks(await loadTasksForAgent(options.db, uniqueAgents[0]), { finishedLimit: 3 }).map((task) => ({
|
|
32926
|
+
agent: uniqueAgents[0],
|
|
32927
|
+
task
|
|
32928
|
+
}));
|
|
32929
|
+
if (selected.length === 0) warnings.push(`No tasks found for agent ${uniqueAgents[0]}; falling back to default selection.`);
|
|
32930
|
+
} else {
|
|
32931
|
+
selected = selectMultiAgentWatchTasks(await Promise.all(uniqueAgents.map(async (agent) => {
|
|
32932
|
+
return (await loadTasksForAgent(options.db, agent)).map((task) => ({
|
|
32933
|
+
agent,
|
|
32934
|
+
task
|
|
32935
|
+
}));
|
|
32936
|
+
})).then((groups) => groups.flat()), uniqueAgents);
|
|
32937
|
+
const foundAgents = new Set(selected.map((t) => t.agent));
|
|
32938
|
+
const missing = uniqueAgents.filter((agent) => !foundAgents.has(agent));
|
|
32939
|
+
if (missing.length > 0) warnings.push(`No tasks found for agent(s): ${missing.join(", ")}`);
|
|
32940
|
+
if (selected.length === 0) warnings.push("No requested agent tasks found; falling back to default selection.");
|
|
32941
|
+
}
|
|
32942
|
+
} else warnings.push("No targets specified; selecting up to 3 agents automatically.");
|
|
32943
|
+
if (selected.length === 0) selected = selectFallbackWatchTasks(await loadAllTasks(options.db, 500), { agentLimit: 3 });
|
|
32944
|
+
const executeAgentMap = await getAgentExecuteAgentMap(options.db, selected);
|
|
32945
|
+
return {
|
|
32946
|
+
targets: selected.map((entry) => ({
|
|
32947
|
+
...entry,
|
|
32948
|
+
executeAgent: executeAgentMap.get(`${entry.agent}|${entry.task.project_id}`)
|
|
32949
|
+
})),
|
|
32950
|
+
warnings
|
|
32951
|
+
};
|
|
32952
|
+
}
|
|
32953
|
+
const ANSI = {
|
|
32954
|
+
reset: "\x1B[0m",
|
|
32955
|
+
bold: "\x1B[1m",
|
|
32956
|
+
dim: "\x1B[2m",
|
|
32957
|
+
red: "\x1B[31m",
|
|
32958
|
+
green: "\x1B[32m",
|
|
32959
|
+
yellow: "\x1B[33m",
|
|
32960
|
+
cyan: "\x1B[36m",
|
|
32961
|
+
gray: "\x1B[90m"
|
|
32962
|
+
};
|
|
32963
|
+
function sgrWrap(text, code, enabled) {
|
|
32964
|
+
return enabled ? `${code}${text}${ANSI.reset}` : text;
|
|
32965
|
+
}
|
|
32966
|
+
function bold(text, enabled) {
|
|
32967
|
+
return sgrWrap(text, ANSI.bold, enabled);
|
|
32968
|
+
}
|
|
32969
|
+
function dimGray(text, enabled) {
|
|
32970
|
+
return sgrWrap(text, `${ANSI.dim}${ANSI.gray}`, enabled);
|
|
32971
|
+
}
|
|
32972
|
+
function wrapRawLineToWidth(line, width) {
|
|
32973
|
+
if (width <= 0) return [line];
|
|
32974
|
+
if (line.length === 0) return [""];
|
|
32975
|
+
const out = [];
|
|
32976
|
+
for (let i = 0; i < line.length; i += width) out.push(line.slice(i, i + width));
|
|
32977
|
+
return out;
|
|
32653
32978
|
}
|
|
32654
32979
|
const TODO_PREVIEW_MAX_ITEMS = 3;
|
|
32655
32980
|
const TODO_PREVIEW_MAX_PENDING = 2;
|
|
@@ -32751,7 +33076,7 @@ function createWatchCommand(version) {
|
|
|
32751
33076
|
rt.streamDone = false;
|
|
32752
33077
|
const preferred = toStreamSourceFromExecuteAgent(rt.target.executeAgent);
|
|
32753
33078
|
rt.source = preferred;
|
|
32754
|
-
const url =
|
|
33079
|
+
const url = makeOpaqueStreamUrl(streamId);
|
|
32755
33080
|
(async () => {
|
|
32756
33081
|
try {
|
|
32757
33082
|
const res = await fetch(url, {
|
|
@@ -32914,86 +33239,12 @@ function createWatchCommand(version) {
|
|
|
32914
33239
|
return runtime[selectedIndex];
|
|
32915
33240
|
}
|
|
32916
33241
|
function getFormattedLogLines(rt, options) {
|
|
32917
|
-
|
|
32918
|
-
|
|
32919
|
-
|
|
32920
|
-
|
|
32921
|
-
|
|
32922
|
-
|
|
32923
|
-
if (lines.length === 0) return;
|
|
32924
|
-
blocks.push({
|
|
32925
|
-
order: order ?? Number.MAX_SAFE_INTEGER,
|
|
32926
|
-
lines
|
|
32927
|
-
});
|
|
32928
|
-
}
|
|
32929
|
-
for (let timelineIndex = 0; timelineIndex < step.timeline.length; timelineIndex++) {
|
|
32930
|
-
const entry = step.timeline[timelineIndex];
|
|
32931
|
-
if (entry.kind === "reasoning") {
|
|
32932
|
-
const reasoningBlock = [];
|
|
32933
|
-
const hasTrailingLineFeed = entry.text.endsWith("\n");
|
|
32934
|
-
const rawReasoningLines = toDisplayLines(entry.text);
|
|
32935
|
-
for (const rawLine of rawReasoningLines) wrapRawLineToWidth(rawLine, reasoningWrapWidth).forEach((chunk) => reasoningBlock.push(gray(chunk, options.style)));
|
|
32936
|
-
if (!hasTrailingLineFeed) reasoningBlock.push("");
|
|
32937
|
-
pushBlock(entry.order, reasoningBlock);
|
|
32938
|
-
continue;
|
|
32939
|
-
}
|
|
32940
|
-
if (entry.kind === "message") {
|
|
32941
|
-
const messageBlock = [];
|
|
32942
|
-
const rawMessageLines = toDisplayLines(entry.text);
|
|
32943
|
-
for (const rawLine of rawMessageLines) wrapRawLineToWidth(rawLine, messageWrapWidth).forEach((chunk) => messageBlock.push(chunk));
|
|
32944
|
-
pushBlock(entry.order, messageBlock);
|
|
32945
|
-
continue;
|
|
32946
|
-
}
|
|
32947
|
-
if (entry.kind === "warning") {
|
|
32948
|
-
pushBlock(entry.order, [kv("warning", yellow(entry.message, options.style), options.style)]);
|
|
32949
|
-
continue;
|
|
32950
|
-
}
|
|
32951
|
-
const tool = toolById.get(entry.toolCallId);
|
|
32952
|
-
if (!tool) continue;
|
|
32953
|
-
const exploredSubLines = getExploredSubCommandLines(tool);
|
|
32954
|
-
if (exploredSubLines != null) {
|
|
32955
|
-
const groupedTools = [tool];
|
|
32956
|
-
const groupedSubLines = [...exploredSubLines];
|
|
32957
|
-
let lookahead = timelineIndex + 1;
|
|
32958
|
-
while (lookahead < step.timeline.length) {
|
|
32959
|
-
const next = step.timeline[lookahead];
|
|
32960
|
-
if (next.kind !== "tool") break;
|
|
32961
|
-
const nextTool = toolById.get(next.toolCallId);
|
|
32962
|
-
if (!nextTool) break;
|
|
32963
|
-
const nextSubLines = getExploredSubCommandLines(nextTool);
|
|
32964
|
-
if (nextSubLines == null) break;
|
|
32965
|
-
groupedTools.push(nextTool);
|
|
32966
|
-
groupedSubLines.push(...nextSubLines);
|
|
32967
|
-
lookahead += 1;
|
|
32968
|
-
}
|
|
32969
|
-
const groupedBlock = [` ${[
|
|
32970
|
-
formatToolStatusIcon(combineToolStatuses(groupedTools.map((groupedTool) => groupedTool.status)), options.style),
|
|
32971
|
-
cyan("Explored", options.style),
|
|
32972
|
-
groupedTools.length > 1 ? gray(`x${groupedTools.length}`, options.style) : void 0
|
|
32973
|
-
].filter(Boolean).join(" ")}`];
|
|
32974
|
-
groupedSubLines.forEach((subLine, subIndex) => groupedBlock.push(subIndex === 0 ? ` ${dimGray("└", options.style)} ${formatExploredSubCommandLine(subLine, options.style)}` : ` ${formatExploredSubCommandLine(subLine, options.style)}`));
|
|
32975
|
-
pushBlock(entry.order, groupedBlock);
|
|
32976
|
-
timelineIndex = lookahead - 1;
|
|
32977
|
-
continue;
|
|
32978
|
-
}
|
|
32979
|
-
const toolBlock = [];
|
|
32980
|
-
toDisplayLines(formatToolSummaryLine(tool, options.style)).forEach((line, index) => toolBlock.push(index === 0 ? ` ${line}` : ` ${line}`));
|
|
32981
|
-
if (tool.toolName === "command_execution") formatToolResultPreviewLines(tool, {
|
|
32982
|
-
maxLines: Number.MAX_SAFE_INTEGER,
|
|
32983
|
-
widthHint: Math.max(20, options.widthHint - 10),
|
|
32984
|
-
style: options.style
|
|
32985
|
-
}).forEach((line, index) => toolBlock.push(index === 0 ? ` ${dimGray("└", options.style)} ${dimGray(line, options.style)}` : ` ${dimGray(line, options.style)}`));
|
|
32986
|
-
pushBlock(entry.order, toolBlock);
|
|
32987
|
-
}
|
|
32988
|
-
if (rt.lastError) pushBlock(void 0, [kv("error", red(rt.lastError, options.style), options.style)]);
|
|
32989
|
-
blocks.sort((a, b) => a.order - b.order);
|
|
32990
|
-
const lines = [];
|
|
32991
|
-
for (const block of blocks) {
|
|
32992
|
-
if (lines.length > 0 && lines[lines.length - 1] !== "") lines.push("");
|
|
32993
|
-
lines.push(...block.lines);
|
|
32994
|
-
}
|
|
32995
|
-
if (lines.length === 0) lines.push(gray("(waiting for formatted stream output)", options.style));
|
|
32996
|
-
return lines;
|
|
33242
|
+
return formatWatchLogLines({
|
|
33243
|
+
snapshot: rt.aggregator.snapshot(),
|
|
33244
|
+
lastError: rt.lastError,
|
|
33245
|
+
style: options.style,
|
|
33246
|
+
widthHint: options.widthHint
|
|
33247
|
+
});
|
|
32997
33248
|
}
|
|
32998
33249
|
function moveTaskSelection(delta) {
|
|
32999
33250
|
if (runtime.length === 0) return;
|
|
@@ -33189,9 +33440,251 @@ function createWatchCommand(version) {
|
|
|
33189
33440
|
});
|
|
33190
33441
|
}
|
|
33191
33442
|
|
|
33443
|
+
//#endregion
|
|
33444
|
+
//#region src/cli/commands/watch-stream.ts
|
|
33445
|
+
function parseSourceOption(value) {
|
|
33446
|
+
try {
|
|
33447
|
+
return parseAgentStreamSourceOption(value);
|
|
33448
|
+
} catch (error) {
|
|
33449
|
+
throw new InvalidArgumentError(error instanceof Error ? error.message : String(error));
|
|
33450
|
+
}
|
|
33451
|
+
}
|
|
33452
|
+
function createWatchStreamCommand(version) {
|
|
33453
|
+
return createCommand("watch-stream").version(version).description("Watch a single stream and show formatted live logs.").argument("<stream-id>", "The stream id to watch.").option("--source <source>", "Force stream source (codex or claude-code).", parseSourceOption).action(async function() {
|
|
33454
|
+
if (!ensureEnv(["PANTHEON_API_KEY"])) return;
|
|
33455
|
+
const [streamId] = this.args;
|
|
33456
|
+
const options = this.opts();
|
|
33457
|
+
if (!process$1.stdout.isTTY) {
|
|
33458
|
+
console.error("watch-stream requires a TTY.");
|
|
33459
|
+
process$1.exit(1);
|
|
33460
|
+
}
|
|
33461
|
+
const aggregator = new WatchStepAggregator({ maxLogLines: Number.POSITIVE_INFINITY });
|
|
33462
|
+
let source = options.source;
|
|
33463
|
+
let streamEnded = false;
|
|
33464
|
+
let abortedByIdleTimeout = false;
|
|
33465
|
+
let lastError;
|
|
33466
|
+
const abortController = new AbortController();
|
|
33467
|
+
const onSigInt = () => abortController.abort();
|
|
33468
|
+
process$1.on("SIGINT", onSigInt);
|
|
33469
|
+
const screen = blessed.screen({
|
|
33470
|
+
smartCSR: true,
|
|
33471
|
+
title: "pantheon-agents watch-stream"
|
|
33472
|
+
});
|
|
33473
|
+
screen.key(["q", "C-c"], () => abortController.abort());
|
|
33474
|
+
const pane = blessed.box({
|
|
33475
|
+
parent: screen,
|
|
33476
|
+
top: 0,
|
|
33477
|
+
left: 0,
|
|
33478
|
+
height: "100%-1",
|
|
33479
|
+
width: "100%",
|
|
33480
|
+
border: "line",
|
|
33481
|
+
tags: false,
|
|
33482
|
+
wrap: false,
|
|
33483
|
+
style: { border: { fg: "cyan" } }
|
|
33484
|
+
});
|
|
33485
|
+
const header = blessed.box({
|
|
33486
|
+
parent: pane,
|
|
33487
|
+
top: 0,
|
|
33488
|
+
left: 1,
|
|
33489
|
+
height: 2,
|
|
33490
|
+
width: "100%-2",
|
|
33491
|
+
tags: false,
|
|
33492
|
+
wrap: false
|
|
33493
|
+
});
|
|
33494
|
+
const body = blessed.box({
|
|
33495
|
+
parent: pane,
|
|
33496
|
+
top: 2,
|
|
33497
|
+
left: 1,
|
|
33498
|
+
width: "100%-2",
|
|
33499
|
+
height: 1,
|
|
33500
|
+
tags: false,
|
|
33501
|
+
wrap: false,
|
|
33502
|
+
scrollable: false
|
|
33503
|
+
});
|
|
33504
|
+
const footer = blessed.box({
|
|
33505
|
+
parent: screen,
|
|
33506
|
+
bottom: 0,
|
|
33507
|
+
left: 0,
|
|
33508
|
+
height: 1,
|
|
33509
|
+
width: "100%",
|
|
33510
|
+
tags: false,
|
|
33511
|
+
wrap: false,
|
|
33512
|
+
style: { fg: "gray" }
|
|
33513
|
+
});
|
|
33514
|
+
const viewport = {
|
|
33515
|
+
autoFollow: true,
|
|
33516
|
+
scrollOffset: 0
|
|
33517
|
+
};
|
|
33518
|
+
function getFormattedLogLines(style, widthHint) {
|
|
33519
|
+
return formatWatchLogLines({
|
|
33520
|
+
snapshot: aggregator.snapshot(),
|
|
33521
|
+
lastError,
|
|
33522
|
+
style,
|
|
33523
|
+
widthHint
|
|
33524
|
+
});
|
|
33525
|
+
}
|
|
33526
|
+
function scrollLog(delta) {
|
|
33527
|
+
const bodyHeight = typeof body.height === "number" ? body.height : 0;
|
|
33528
|
+
if (bodyHeight <= 0) return;
|
|
33529
|
+
const bodyWidth = typeof body.width === "number" ? body.width : 80;
|
|
33530
|
+
const lines = getFormattedLogLines(useAnsiStyles(), Math.max(20, bodyWidth));
|
|
33531
|
+
const maxOffset = Math.max(0, lines.length - bodyHeight);
|
|
33532
|
+
viewport.autoFollow = false;
|
|
33533
|
+
viewport.scrollOffset = Math.max(0, Math.min(maxOffset, viewport.scrollOffset + delta));
|
|
33534
|
+
}
|
|
33535
|
+
function scrollToTop() {
|
|
33536
|
+
viewport.autoFollow = false;
|
|
33537
|
+
viewport.scrollOffset = 0;
|
|
33538
|
+
}
|
|
33539
|
+
function scrollToBottom() {
|
|
33540
|
+
const bodyHeight = typeof body.height === "number" ? body.height : 0;
|
|
33541
|
+
if (bodyHeight <= 0) return;
|
|
33542
|
+
const bodyWidth = typeof body.width === "number" ? body.width : 80;
|
|
33543
|
+
const lines = getFormattedLogLines(useAnsiStyles(), Math.max(20, bodyWidth));
|
|
33544
|
+
const maxOffset = Math.max(0, lines.length - bodyHeight);
|
|
33545
|
+
viewport.autoFollow = false;
|
|
33546
|
+
viewport.scrollOffset = maxOffset;
|
|
33547
|
+
}
|
|
33548
|
+
function getHalfScreenScrollStep() {
|
|
33549
|
+
const bodyHeight = typeof body.height === "number" ? body.height : 0;
|
|
33550
|
+
return Math.max(1, Math.floor(bodyHeight / 2));
|
|
33551
|
+
}
|
|
33552
|
+
screen.key(["up"], () => {
|
|
33553
|
+
scrollLog(-getHalfScreenScrollStep());
|
|
33554
|
+
});
|
|
33555
|
+
screen.key(["down"], () => {
|
|
33556
|
+
scrollLog(getHalfScreenScrollStep());
|
|
33557
|
+
});
|
|
33558
|
+
screen.key(["g", "home"], () => {
|
|
33559
|
+
scrollToTop();
|
|
33560
|
+
});
|
|
33561
|
+
screen.key([
|
|
33562
|
+
"G",
|
|
33563
|
+
"S-g",
|
|
33564
|
+
"end"
|
|
33565
|
+
], () => {
|
|
33566
|
+
scrollToBottom();
|
|
33567
|
+
});
|
|
33568
|
+
screen.key(["f"], () => {
|
|
33569
|
+
viewport.autoFollow = true;
|
|
33570
|
+
});
|
|
33571
|
+
const renderTimer = setInterval(() => {
|
|
33572
|
+
const style = useAnsiStyles();
|
|
33573
|
+
const columns = Math.max(40, screen.width ?? 120);
|
|
33574
|
+
const rows = Math.max(8, screen.height ?? 24);
|
|
33575
|
+
const contentHeight = Math.max(1, rows - 1);
|
|
33576
|
+
pane.top = 0;
|
|
33577
|
+
pane.left = 0;
|
|
33578
|
+
pane.width = columns;
|
|
33579
|
+
pane.height = contentHeight;
|
|
33580
|
+
pane.setLabel(" Stream Logs ");
|
|
33581
|
+
const paneInnerWidth = Math.max(1, columns - 2);
|
|
33582
|
+
const paneInnerHeight = Math.max(1, contentHeight - 2);
|
|
33583
|
+
const headerHeight = Math.min(2, paneInnerHeight);
|
|
33584
|
+
const bodyHeight = Math.max(0, paneInnerHeight - headerHeight);
|
|
33585
|
+
header.top = 0;
|
|
33586
|
+
header.left = 1;
|
|
33587
|
+
header.width = paneInnerWidth;
|
|
33588
|
+
header.height = headerHeight;
|
|
33589
|
+
const headerLines = [clip(`stream id: ${streamId}`, paneInnerWidth), clip(`source: ${source ?? "(detecting...)"} • status: ${streamEnded ? "ended" : "streaming"}${abortedByIdleTimeout ? " (idle timeout)" : ""}`, paneInnerWidth)];
|
|
33590
|
+
header.setContent(headerLines.slice(0, headerHeight).join("\n"));
|
|
33591
|
+
body.top = headerHeight;
|
|
33592
|
+
body.left = 1;
|
|
33593
|
+
body.width = paneInnerWidth;
|
|
33594
|
+
body.height = bodyHeight;
|
|
33595
|
+
const lines = getFormattedLogLines(style, paneInnerWidth);
|
|
33596
|
+
const maxOffset = Math.max(0, lines.length - bodyHeight);
|
|
33597
|
+
if (viewport.autoFollow) viewport.scrollOffset = maxOffset;
|
|
33598
|
+
else viewport.scrollOffset = Math.max(0, Math.min(maxOffset, viewport.scrollOffset));
|
|
33599
|
+
if (bodyHeight <= 0) body.hide();
|
|
33600
|
+
else {
|
|
33601
|
+
const visibleLines = lines.slice(viewport.scrollOffset, viewport.scrollOffset + bodyHeight);
|
|
33602
|
+
body.setContent(visibleLines.map((line) => clip(line, paneInnerWidth)).join("\n"));
|
|
33603
|
+
body.show();
|
|
33604
|
+
}
|
|
33605
|
+
const footerParts = [
|
|
33606
|
+
"Ctrl+C to exit",
|
|
33607
|
+
"q to quit",
|
|
33608
|
+
"↑/↓ half-page",
|
|
33609
|
+
"g/G top/bottom",
|
|
33610
|
+
"f resume auto-scroll"
|
|
33611
|
+
];
|
|
33612
|
+
if (!viewport.autoFollow) footerParts.push("auto-scroll paused");
|
|
33613
|
+
if (lastError) footerParts.push(`error: ${clip(lastError, 40)}`);
|
|
33614
|
+
footer.setContent(footerParts.join(" • "));
|
|
33615
|
+
screen.render();
|
|
33616
|
+
}, 300);
|
|
33617
|
+
const streamAbort = new AbortController();
|
|
33618
|
+
(async () => {
|
|
33619
|
+
try {
|
|
33620
|
+
const res = await fetch(makeOpaqueStreamUrl(streamId), {
|
|
33621
|
+
headers: {
|
|
33622
|
+
Authorization: `Bearer ${process$1.env.PANTHEON_API_KEY}`,
|
|
33623
|
+
Accept: "text/event-stream"
|
|
33624
|
+
},
|
|
33625
|
+
signal: streamAbort.signal
|
|
33626
|
+
});
|
|
33627
|
+
if (!res.ok) {
|
|
33628
|
+
lastError = `stream request failed: ${res.status} ${res.statusText}`;
|
|
33629
|
+
streamEnded = true;
|
|
33630
|
+
return;
|
|
33631
|
+
}
|
|
33632
|
+
if (!res.body) {
|
|
33633
|
+
lastError = "missing response body";
|
|
33634
|
+
streamEnded = true;
|
|
33635
|
+
return;
|
|
33636
|
+
}
|
|
33637
|
+
let normalizer;
|
|
33638
|
+
const consumeResult = await consumeOpaqueSseStream({
|
|
33639
|
+
stream: res.body,
|
|
33640
|
+
signal: streamAbort.signal,
|
|
33641
|
+
onWarning: (warning) => aggregator.pushUiChunk({
|
|
33642
|
+
type: "data-agent-unknown",
|
|
33643
|
+
data: { reason: warning }
|
|
33644
|
+
}),
|
|
33645
|
+
onData: (payload) => {
|
|
33646
|
+
if (!normalizer) {
|
|
33647
|
+
source = source ?? detectStreamSourceFromRawPayload(payload) ?? "codex";
|
|
33648
|
+
normalizer = createAgentStreamNormalizer({
|
|
33649
|
+
source,
|
|
33650
|
+
emitStartChunk: false,
|
|
33651
|
+
includeMetaEvents: true,
|
|
33652
|
+
unknownChunkPolicy: "emit"
|
|
33653
|
+
});
|
|
33654
|
+
}
|
|
33655
|
+
aggregator.pushUiChunks(normalizer.push(payload));
|
|
33656
|
+
}
|
|
33657
|
+
});
|
|
33658
|
+
if (normalizer) aggregator.pushUiChunks(normalizer.finish());
|
|
33659
|
+
streamEnded = consumeResult.ended;
|
|
33660
|
+
abortedByIdleTimeout = consumeResult.abortedByIdleTimeout;
|
|
33661
|
+
} catch (error) {
|
|
33662
|
+
if (streamAbort.signal.aborted) {
|
|
33663
|
+
streamEnded = true;
|
|
33664
|
+
return;
|
|
33665
|
+
}
|
|
33666
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
33667
|
+
streamEnded = true;
|
|
33668
|
+
}
|
|
33669
|
+
})();
|
|
33670
|
+
try {
|
|
33671
|
+
await new Promise((resolve) => {
|
|
33672
|
+
const resolveOnce = () => resolve();
|
|
33673
|
+
abortController.signal.addEventListener("abort", resolveOnce, { once: true });
|
|
33674
|
+
if (abortController.signal.aborted) resolveOnce();
|
|
33675
|
+
});
|
|
33676
|
+
} finally {
|
|
33677
|
+
process$1.off("SIGINT", onSigInt);
|
|
33678
|
+
clearInterval(renderTimer);
|
|
33679
|
+
streamAbort.abort();
|
|
33680
|
+
screen.destroy();
|
|
33681
|
+
}
|
|
33682
|
+
});
|
|
33683
|
+
}
|
|
33684
|
+
|
|
33192
33685
|
//#endregion
|
|
33193
33686
|
//#region src/cli/index.ts
|
|
33194
|
-
const program = new Command().name("pantheon-agents").description("Pantheon agents CLI").version(version$1).showHelpAfterError().showSuggestionAfterError().addHelpCommand().addCommand(createAddTaskCommand(version$1)).addCommand(createConfigAgentCommand(version$1)).addCommand(createDeleteTaskCommand(version$1)).addCommand(createCancelTaskCommand(version$1)).addCommand(createReconfigAgentCommand(version$1)).addCommand(createRunAgentCommand(version$1)).addCommand(createServerCommand(version$1)).addCommand(createShowConfigCommand(version$1)).addCommand(createShowTasksCommand(version$1)).addCommand(createWatchCommand(version$1)).addCommand(createLlmExplainCommand(version$1));
|
|
33687
|
+
const program = new Command().name("pantheon-agents").description("Pantheon agents CLI").version(version$1).showHelpAfterError().showSuggestionAfterError().addHelpCommand().addCommand(createAddTaskCommand(version$1)).addCommand(createConfigAgentCommand(version$1)).addCommand(createDeleteTaskCommand(version$1)).addCommand(createCancelTaskCommand(version$1)).addCommand(createReconfigAgentCommand(version$1)).addCommand(createRunAgentCommand(version$1)).addCommand(createServerCommand(version$1)).addCommand(createShowConfigCommand(version$1)).addCommand(createShowTasksCommand(version$1)).addCommand(createWatchCommand(version$1)).addCommand(createWatchStreamCommand(version$1)).addCommand(createLlmExplainCommand(version$1));
|
|
33195
33688
|
async function main() {
|
|
33196
33689
|
if (process$1.argv.length <= 2) {
|
|
33197
33690
|
program.outputHelp();
|