@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.
Files changed (3) hide show
  1. package/README.md +8 -4
  2. package/dist/index.js +1142 -649
  3. 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.0.17";
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: [prompt],
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: taskToStart.task,
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
- if (options.bootstrap) {
17639
- const { branch_id: branchId, latest_snap_id: branchLatestSnapId } = await executeOnPantheon({
17640
- projectId: options.projectId,
17641
- branchId: options.rootBranchId,
17642
- agent: "codex",
17643
- prompt: `You must follow these instructions":
17644
- 1. Clone the main branch from ${options.prototypeUrl} to a temporary directory (<pantheon-agents> in follow instructions references to this directory).
17645
- 2. Copy the <pantheon-agents>/agents/${options.role}/AGENTS.md to \`<workspace>/AGENTS.md\`.
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: options.rootBranchId,
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("<project-id>", "The project id of the agent.").argument("<task-prompt>", "The prompt of the task.").option("--parent-task-id <task-id>", "Parent task id for dependent tasks.").action(async function() {
17971
- const [name, projectId, taskPrompt] = this.args;
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.").argument("<project-id>", "The project id of the agent.").option("--skills <skills>", "The skills of the agent. Multiple values are separated by comma.", (val) => val.split(",").map((s) => s.trim()).filter((s) => s !== ""), []).option("--execute-agent <agent>", "The execute agent of the agent.", "codex").option("--root-branch-id <branchId>", "The root branch id of the agent. Default to project root branch id.").option("--prototype-url <url>", "Role and skill definitions repo.", "https://github.com/pingcap-inc/pantheon-agents").option("--no-bootstrap", "Prevent bootstrap base branch for agent. Use the root branch as base branch.").action(async function() {
18031
- const [name, role, projectId] = this.args;
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 (options.bootstrap || !options.rootBranchId) requiredEnvVars.push("PANTHEON_API_KEY");
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.").argument("<project-id>", "The project id of the agent.").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() {
18233
- const [name, projectId] = this.args;
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 getBranchOutputOrThrow(options) {
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
- const bootstrapOutput = await getBranchOutputOrThrow({
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 reconfigAgentConfigWithBootstrapOutput(options) {
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 getAgentConfigWithBootstrapOutput({
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 reconfigAgentConfigWithBootstrapOutput({
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, including the bootstrap branch output.",
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 getAgentConfigWithBootstrapOutput({
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 reconfigAgentConfigWithBootstrapOutput({
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.").argument("[project-id]", "The project id.").option("--json", "Output config as JSON.").action(async function() {
31125
- const [name, projectId] = this.args;
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
- const topLevelSchema = z$1.object({ type: z$1.string() }).passthrough();
31764
- const streamEventSchema = z$1.object({
31765
- type: z$1.string(),
31766
- index: z$1.number().optional()
31767
- }).passthrough();
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
- let sawStreamEvents = false;
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 ensureTextBlock(index) {
31781
- const existing = textBlocks.get(index);
31782
- if (existing?.open) return [];
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
- const index = typeof event.index === "number" ? event.index : void 0;
31815
- switch (event.type) {
31816
- case "content_block_start": {
31817
- if (index == null) return [];
31818
- const block = event.content_block;
31819
- if (!isRecord(block) || typeof block.type !== "string") return [];
31820
- sawStreamEvents = true;
31821
- if (block.type === "text") out.push(...ensureTextBlock(index));
31822
- else if (block.type === "tool_use") out.push(...ensureToolBlock(index, {
31823
- id: typeof block.id === "string" ? block.id : void 0,
31824
- name: typeof block.name === "string" ? block.name : void 0
31825
- }));
31826
- return out;
31827
- }
31828
- case "content_block_delta": {
31829
- if (index == null) return [];
31830
- const delta = event.delta;
31831
- if (!isRecord(delta) || typeof delta.type !== "string") return [];
31832
- sawStreamEvents = true;
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-end",
31868
- id: text.id
31807
+ type: "text-start",
31808
+ id: state.id
31869
31809
  });
31870
31810
  }
31871
- const tool = toolBlocks.get(index);
31872
- if (tool?.open) {
31873
- tool.open = false;
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: "tool-input-available",
31881
- toolCallId: tool.toolCallId,
31882
- toolName: tool.toolName,
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 (block.type === "tool_use") {
31916
- const toolCallId = typeof block.id === "string" ? block.id : `claude-assistant-tool-${index}`;
31917
- const toolName = typeof block.name === "string" ? block.name : "tool";
31918
- const input = block.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 = Array.isArray(message.content) ? message.content : [];
31886
+ const blocks = getMessageContentBlocks(message);
31952
31887
  const out = [];
31953
31888
  blocks.forEach((block, index) => {
31954
- if (!isRecord(block) || block.type !== "tool_result") return;
31955
- const toolCallId = typeof block.tool_use_id === "string" ? block.tool_use_id : `claude-tool-result-${index}`;
31956
- const content = block.content;
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
- if (block.is_error === true || interrupted) {
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
- const parsed = topLevelSchema.safeParse(chunk);
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 = parsed.data;
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
- const event = streamEventSchema.safeParse(msg.event);
32008
- if (!event.success) return {
32009
- recognized: false,
32010
- chunks: []
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
- case "user": {
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 PANTHEON_BASE_URL = "https://pantheon-ai.tidb.ai";
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 useAnsiStyles() {
32455
- if (!process.stdout.isTTY) return false;
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 sgrWrap(text, code, enabled) {
32460
- return enabled ? `${code}${text}${ANSI.reset}` : text;
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 colorizeCommandProgram(display, style) {
32495
- const match = display.match(/^(\s*)(\S+)([\s\S]*)$/);
32496
- if (!match) return display;
32497
- const [, leading, program, rest] = match;
32498
- return `${leading}${cyan(program ?? "", style)}${rest ?? ""}`;
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 getCommandExecutionRawCommand(tool) {
32501
- const title = typeof tool.title === "string" ? tool.title : "";
32502
- const inputCommand = typeof tool.input?.command === "string" ? tool.input.command : "";
32503
- return title || inputCommand || void 0;
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 (name === "command_execution") {
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
- const display = getCommandExecutionDisplayCommand(tool) ?? "(unknown command)";
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 !== "command_execution") return void 0;
32546
- const rawCommand = getCommandExecutionRawCommand(tool);
32547
- if (!rawCommand) return void 0;
32548
- const exploredLines = getExploredCommandLines(rawCommand);
32549
- if (!exploredLines || exploredLines.length <= 1) return void 0;
32550
- return exploredLines.slice(1);
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 === "command_execution") {
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 === "command_execution" ? trimCommandOutputTopBottom(lines) : lines, options.maxLines);
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 === "command_execution") {
32611
- const output = tool.output;
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[") ? ANSI.reset : ""}`;
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 = makeStreamUrl(streamId);
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
- const step = rt.aggregator.snapshot();
32918
- const blocks = [];
32919
- const reasoningWrapWidth = Math.max(1, options.widthHint);
32920
- const messageWrapWidth = Math.max(1, options.widthHint);
32921
- const toolById = new Map(step.toolActions.map((tool) => [tool.toolCallId, tool]));
32922
- function pushBlock(order, lines) {
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();