opencode-supertask 0.1.39 → 0.1.40

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.
@@ -6005,6 +6005,7 @@ var init_schema = __esm({
6005
6005
  name: text("name").notNull(),
6006
6006
  agent: text("agent").notNull(),
6007
6007
  model: text("model").default("default"),
6008
+ variant: text("variant"),
6008
6009
  prompt: text("prompt").notNull(),
6009
6010
  cwd: text("cwd"),
6010
6011
  // 分类与优先级
@@ -6049,6 +6050,7 @@ var init_schema = __esm({
6049
6050
  taskId: integer("task_id").notNull().references(() => tasks.id, { onDelete: "cascade" }),
6050
6051
  sessionId: text("session_id"),
6051
6052
  model: text("model"),
6053
+ variant: text("variant"),
6052
6054
  status: text("status").default("running"),
6053
6055
  startedAt: integer("started_at", { mode: "timestamp" }).$defaultFn(() => /* @__PURE__ */ new Date()),
6054
6056
  finishedAt: integer("finished_at", { mode: "timestamp" }),
@@ -6069,6 +6071,7 @@ var init_schema = __esm({
6069
6071
  name: text("name").notNull(),
6070
6072
  agent: text("agent").notNull(),
6071
6073
  model: text("model").default("default"),
6074
+ variant: text("variant"),
6072
6075
  prompt: text("prompt").notNull(),
6073
6076
  cwd: text("cwd"),
6074
6077
  category: text("category").default("general"),
@@ -6439,6 +6442,28 @@ var init_task_batch = __esm({
6439
6442
  }
6440
6443
  });
6441
6444
 
6445
+ // src/core/model-variant.ts
6446
+ function normalizeModelVariant(value) {
6447
+ if (value === void 0 || value === null) return value;
6448
+ const normalized = value.trim();
6449
+ if (!normalized) return null;
6450
+ if (normalized.length > MAX_MODEL_VARIANT_LENGTH) {
6451
+ throw new Error(`variant \u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7 ${MAX_MODEL_VARIANT_LENGTH} \u4E2A\u5B57\u7B26`);
6452
+ }
6453
+ if (CONTROL_CHARACTER_PATTERN.test(normalized)) {
6454
+ throw new Error("variant \u4E0D\u80FD\u5305\u542B\u63A7\u5236\u5B57\u7B26");
6455
+ }
6456
+ return normalized;
6457
+ }
6458
+ var MAX_MODEL_VARIANT_LENGTH, CONTROL_CHARACTER_PATTERN;
6459
+ var init_model_variant = __esm({
6460
+ "src/core/model-variant.ts"() {
6461
+ "use strict";
6462
+ MAX_MODEL_VARIANT_LENGTH = 128;
6463
+ CONTROL_CHARACTER_PATTERN = /[\u0000-\u001F\u007F]/;
6464
+ }
6465
+ });
6466
+
6442
6467
  // src/core/services/task.service.ts
6443
6468
  function hasNoExecutableDependents() {
6444
6469
  return sql`NOT EXISTS (
@@ -6494,6 +6519,7 @@ var init_task_service = __esm({
6494
6519
  init_backoff();
6495
6520
  init_task_working_directory();
6496
6521
  init_task_batch();
6522
+ init_model_variant();
6497
6523
  ({ tasks: tasks2, taskRuns: taskRuns2 } = schema_exports);
6498
6524
  cleanupInvocation = 0;
6499
6525
  TaskDeletionConflictError = class extends Error {
@@ -6513,7 +6539,8 @@ var init_task_service = __esm({
6513
6539
  static async add(data) {
6514
6540
  const normalizedData = {
6515
6541
  ...data,
6516
- batchId: normalizeTaskBatchId(data.batchId)
6542
+ batchId: normalizeTaskBatchId(data.batchId),
6543
+ variant: normalizeModelVariant(data.variant)
6517
6544
  };
6518
6545
  this.validateNewTask(normalizedData);
6519
6546
  return db.transaction((tx) => {
@@ -6541,7 +6568,11 @@ var init_task_service = __esm({
6541
6568
  }
6542
6569
  static async update(id, data, scope = {}) {
6543
6570
  if (Object.keys(data).length === 0) throw new Error("\u81F3\u5C11\u63D0\u4F9B\u4E00\u4E2A\u8981\u4FEE\u6539\u7684\u5B57\u6BB5");
6544
- const normalizedData = data.batchId === void 0 ? data : { ...data, batchId: normalizeTaskBatchId(data.batchId) ?? null };
6571
+ const normalizedData = {
6572
+ ...data,
6573
+ ...data.batchId === void 0 ? {} : { batchId: normalizeTaskBatchId(data.batchId) ?? null },
6574
+ ...data.variant === void 0 ? {} : { variant: normalizeModelVariant(data.variant) ?? null }
6575
+ };
6545
6576
  return db.transaction((tx) => {
6546
6577
  const task = tx.select().from(tasks2).where(and(
6547
6578
  eq(tasks2.id, id),
@@ -6553,6 +6584,7 @@ var init_task_service = __esm({
6553
6584
  name: normalizedData.name ?? task.name,
6554
6585
  agent: normalizedData.agent ?? task.agent,
6555
6586
  model: normalizedData.model ?? task.model,
6587
+ variant: normalizedData.variant === void 0 ? task.variant : normalizedData.variant,
6556
6588
  prompt: normalizedData.prompt ?? task.prompt,
6557
6589
  cwd: task.cwd,
6558
6590
  category: normalizedData.category ?? task.category,
@@ -17045,12 +17077,14 @@ var init_task_template_service = __esm({
17045
17077
  init_cron_parser();
17046
17078
  init_task_working_directory();
17047
17079
  init_task_batch();
17080
+ init_model_variant();
17048
17081
  ({ taskTemplates: taskTemplates2 } = schema_exports);
17049
17082
  TaskTemplateService = class {
17050
17083
  static async create(data) {
17051
17084
  const normalizedData = {
17052
17085
  ...data,
17053
- batchId: normalizeTaskBatchId(data.batchId)
17086
+ batchId: normalizeTaskBatchId(data.batchId),
17087
+ variant: normalizeModelVariant(data.variant)
17054
17088
  };
17055
17089
  this.validate(normalizedData);
17056
17090
  const now = Date.now();
@@ -17116,7 +17150,8 @@ var init_task_template_service = __esm({
17116
17150
  static async update(id, data) {
17117
17151
  const normalizedData = {
17118
17152
  ...data,
17119
- batchId: normalizeTaskBatchId(data.batchId) ?? null
17153
+ batchId: normalizeTaskBatchId(data.batchId) ?? null,
17154
+ ...data.variant === void 0 ? {} : { variant: normalizeModelVariant(data.variant) ?? null }
17120
17155
  };
17121
17156
  this.validate(normalizedData);
17122
17157
  const now = Date.now();
@@ -17279,6 +17314,7 @@ function createTaskFromTemplate(templateId, options) {
17279
17314
  name: `${options.namePrefix ?? ""}${tmpl.name}`,
17280
17315
  agent: tmpl.agent,
17281
17316
  model: tmpl.model ?? "default",
17317
+ variant: tmpl.variant,
17282
17318
  prompt: tmpl.prompt,
17283
17319
  cwd: tmpl.cwd ?? null,
17284
17320
  category: tmpl.category ?? "general",
@@ -19638,17 +19674,49 @@ function runOpenCode(executable, args, cwd, timeoutMs) {
19638
19674
  const child = spawn2(executable, args, {
19639
19675
  cwd,
19640
19676
  env: process.env,
19641
- stdio: ["ignore", "pipe", "pipe"]
19677
+ stdio: ["ignore", "pipe", "pipe"],
19678
+ detached: process.platform !== "win32"
19642
19679
  });
19643
19680
  let stdout = "";
19644
19681
  let stderr = "";
19645
19682
  let failure = null;
19646
- let finished = false;
19683
+ let settled = false;
19684
+ let forceKillTimer = null;
19685
+ let finalRejectTimer = null;
19686
+ let timer;
19687
+ const signalProcessTree = (signal) => {
19688
+ if (process.platform !== "win32" && child.pid) {
19689
+ try {
19690
+ process.kill(-child.pid, signal);
19691
+ return;
19692
+ } catch {
19693
+ }
19694
+ }
19695
+ child.kill(signal);
19696
+ };
19697
+ const rejectOnce = (error) => {
19698
+ failure ??= error;
19699
+ if (settled) return;
19700
+ settled = true;
19701
+ clearTimeout(timer);
19702
+ if (forceKillTimer) clearTimeout(forceKillTimer);
19703
+ if (finalRejectTimer) clearTimeout(finalRejectTimer);
19704
+ reject(failure);
19705
+ };
19706
+ const terminate = (error) => {
19707
+ if (failure) return;
19708
+ failure = error;
19709
+ signalProcessTree("SIGTERM");
19710
+ forceKillTimer = setTimeout(() => {
19711
+ signalProcessTree("SIGKILL");
19712
+ rejectOnce(error);
19713
+ }, 1e3);
19714
+ finalRejectTimer = setTimeout(() => rejectOnce(error), 2e3);
19715
+ };
19647
19716
  const append = (current, chunk) => {
19648
19717
  const next = current + chunk.toString();
19649
19718
  if (Buffer.byteLength(next) > MAX_OUTPUT_BYTES && failure === null) {
19650
- failure = new Error(`OpenCode \u8F93\u51FA\u8D85\u8FC7 ${MAX_OUTPUT_BYTES} bytes`);
19651
- child.kill("SIGTERM");
19719
+ terminate(new Error(`OpenCode \u8F93\u51FA\u8D85\u8FC7 ${MAX_OUTPUT_BYTES} bytes`));
19652
19720
  }
19653
19721
  return next.slice(-MAX_OUTPUT_BYTES);
19654
19722
  };
@@ -19659,23 +19727,25 @@ function runOpenCode(executable, args, cwd, timeoutMs) {
19659
19727
  stderr = append(stderr, chunk);
19660
19728
  });
19661
19729
  child.once("error", (error) => {
19662
- failure ??= error;
19730
+ if (forceKillTimer) return;
19731
+ rejectOnce(error);
19663
19732
  });
19664
- const timer = setTimeout(() => {
19665
- failure ??= new Error(`OpenCode \u547D\u4EE4\u8D85\u8FC7 ${timeoutMs}ms \u672A\u5B8C\u6210`);
19666
- child.kill("SIGTERM");
19733
+ timer = setTimeout(() => {
19734
+ terminate(new Error(`OpenCode \u547D\u4EE4\u8D85\u8FC7 ${timeoutMs}ms \u672A\u5B8C\u6210`));
19667
19735
  }, timeoutMs);
19668
19736
  child.once("close", (code) => {
19669
- if (finished) return;
19670
- finished = true;
19671
19737
  clearTimeout(timer);
19738
+ if (forceKillTimer) return;
19739
+ if (finalRejectTimer) clearTimeout(finalRejectTimer);
19740
+ if (settled) return;
19741
+ settled = true;
19672
19742
  if (failure) {
19673
19743
  reject(failure);
19674
19744
  return;
19675
19745
  }
19676
19746
  if (code !== 0) {
19677
19747
  const detail = cleanOutput(stderr).trim() || `\u9000\u51FA\u7801 ${code ?? "null"}`;
19678
- reject(new Error(`OpenCode ${args.join(" ")} \u5931\u8D25\uFF1A${detail}`));
19748
+ reject(new OpenCodeCommandExitError(`OpenCode ${args.join(" ")} \u5931\u8D25\uFF1A${detail}`));
19679
19749
  return;
19680
19750
  }
19681
19751
  resolve4(cleanOutput(stdout));
@@ -19685,6 +19755,52 @@ function runOpenCode(executable, args, cwd, timeoutMs) {
19685
19755
  function parseOpenCodeModels(output) {
19686
19756
  return [...new Set(cleanOutput(output).split("\n").map((line) => line.trim()).filter((line) => /^[^\s/]+\/.+/.test(line)))].sort((left, right) => left.localeCompare(right));
19687
19757
  }
19758
+ function isRecord(value) {
19759
+ return typeof value === "object" && value !== null && !Array.isArray(value);
19760
+ }
19761
+ function findJsonObjectEnd(value, start) {
19762
+ let depth = 0;
19763
+ let inString = false;
19764
+ let escaped = false;
19765
+ for (let index2 = start; index2 < value.length; index2 += 1) {
19766
+ const character = value[index2];
19767
+ if (inString) {
19768
+ if (escaped) escaped = false;
19769
+ else if (character === "\\") escaped = true;
19770
+ else if (character === '"') inString = false;
19771
+ continue;
19772
+ }
19773
+ if (character === '"') inString = true;
19774
+ else if (character === "{") depth += 1;
19775
+ else if (character === "}" && --depth === 0) return index2 + 1;
19776
+ }
19777
+ return null;
19778
+ }
19779
+ function parseOpenCodeModelMetadata(output) {
19780
+ const cleaned = cleanOutput(output);
19781
+ const models = /* @__PURE__ */ new Set();
19782
+ const variantsByModel = {};
19783
+ const headingPattern = /^([^\s/]+\/[^\r\n]+)\r?\n\s*(?=\{)/gm;
19784
+ for (const match2 of cleaned.matchAll(headingPattern)) {
19785
+ const model = match2[1].trim();
19786
+ models.add(model);
19787
+ let objectStart = (match2.index ?? 0) + match2[0].length;
19788
+ while (/\s/.test(cleaned[objectStart] ?? "")) objectStart += 1;
19789
+ if (cleaned[objectStart] !== "{") continue;
19790
+ const objectEnd = findJsonObjectEnd(cleaned, objectStart);
19791
+ if (objectEnd === null) continue;
19792
+ try {
19793
+ const metadata = JSON.parse(cleaned.slice(objectStart, objectEnd));
19794
+ if (!isRecord(metadata) || !isRecord(metadata.variants)) continue;
19795
+ variantsByModel[model] = Object.keys(metadata.variants).filter((variant) => variant.trim().length > 0).sort((left, right) => left.localeCompare(right));
19796
+ } catch {
19797
+ }
19798
+ }
19799
+ return {
19800
+ models: [...models].sort((left, right) => left.localeCompare(right)),
19801
+ variantsByModel
19802
+ };
19803
+ }
19688
19804
  function parseOpenCodeAgents(output) {
19689
19805
  const agents = /* @__PURE__ */ new Map();
19690
19806
  for (const line of cleanOutput(output).split("\n")) {
@@ -19708,14 +19824,18 @@ async function loadOpenCodeCatalog(cwd, options = {}) {
19708
19824
  return cached.result;
19709
19825
  }
19710
19826
  const result = Promise.all([
19711
- runOpenCode(executable, ["models"], cwd, timeoutMs),
19827
+ runOpenCode(executable, ["models", "--verbose"], cwd, timeoutMs).catch((error) => {
19828
+ if (!(error instanceof OpenCodeCommandExitError)) throw error;
19829
+ return runOpenCode(executable, ["models"], cwd, timeoutMs);
19830
+ }),
19712
19831
  runOpenCode(executable, ["agent", "list"], cwd, timeoutMs)
19713
19832
  ]).then(([modelsOutput, agentsOutput]) => {
19714
- const models = parseOpenCodeModels(modelsOutput);
19833
+ const metadata = parseOpenCodeModelMetadata(modelsOutput);
19834
+ const models = metadata.models.length > 0 ? metadata.models : parseOpenCodeModels(modelsOutput);
19715
19835
  const agents = parseOpenCodeAgents(agentsOutput).filter((agent) => agent.mode !== "subagent");
19716
19836
  if (models.length === 0) throw new Error("OpenCode \u6CA1\u6709\u8FD4\u56DE\u53EF\u7528\u6A21\u578B");
19717
19837
  if (agents.length === 0) throw new Error("OpenCode \u6CA1\u6709\u8FD4\u56DE\u53EF\u76F4\u63A5\u8FD0\u884C\u7684\u4E3B Agent");
19718
- return { cwd, models, agents };
19838
+ return { cwd, models, variantsByModel: metadata.variantsByModel, agents };
19719
19839
  });
19720
19840
  if (options.useCache !== false) {
19721
19841
  catalogCache.set(cacheKey, { expiresAt: Date.now() + CATALOG_CACHE_MS, result });
@@ -19725,7 +19845,7 @@ async function loadOpenCodeCatalog(cwd, options = {}) {
19725
19845
  }
19726
19846
  return result;
19727
19847
  }
19728
- var CATALOG_CACHE_MS, COMMAND_TIMEOUT_MS, MAX_OUTPUT_BYTES, ANSI_PATTERN, catalogCache;
19848
+ var CATALOG_CACHE_MS, COMMAND_TIMEOUT_MS, MAX_OUTPUT_BYTES, ANSI_PATTERN, OpenCodeCommandExitError, catalogCache;
19729
19849
  var init_opencode_catalog = __esm({
19730
19850
  "src/core/opencode-catalog.ts"() {
19731
19851
  "use strict";
@@ -19734,6 +19854,8 @@ var init_opencode_catalog = __esm({
19734
19854
  COMMAND_TIMEOUT_MS = 2e4;
19735
19855
  MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
19736
19856
  ANSI_PATTERN = /\x1B(?:[@-_][0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g;
19857
+ OpenCodeCommandExitError = class extends Error {
19858
+ };
19737
19859
  catalogCache = /* @__PURE__ */ new Map();
19738
19860
  }
19739
19861
  });
@@ -20721,6 +20843,7 @@ function clientMessages(locale) {
20721
20843
  "details.copySuccess",
20722
20844
  "details.id",
20723
20845
  "details.project",
20846
+ "details.variant",
20724
20847
  "details.prompt",
20725
20848
  "details.result",
20726
20849
  "details.category",
@@ -20821,6 +20944,7 @@ function clientMessages(locale) {
20821
20944
  "catalog.chooseProject",
20822
20945
  "catalog.defaultModel",
20823
20946
  "catalog.defaultProvider",
20947
+ "catalog.defaultVariant",
20824
20948
  "catalog.loading",
20825
20949
  "catalog.loaded",
20826
20950
  "catalog.failed",
@@ -20945,23 +21069,23 @@ function renderLayout(options) {
20945
21069
  function detailSession(value){if(!value)return text('details.none');const session=String(value);return session.length<=10?session:session.slice(0,6)+'***'+session.slice(-4)}
20946
21070
  function detailTaskResult(data){const presentation=data._resultPresentation;if(!presentation)return text('details.none');const parts=[];if(Array.isArray(presentation.errors)&&presentation.errors.length)parts.push(presentation.errors.join('\\n'));if(presentation.text)parts.push(presentation.text);return parts.join('\\n\\n')||text('details.none')}
20947
21071
  function detailField(label,value,options={}){const item=document.createElement('div');item.className='detail-item'+(options.wide?' wide':'');const name=document.createElement('div');name.className='detail-label';name.textContent=label;const content=options.long?document.createElement('pre'):document.createElement('div');content.className='detail-value'+(options.mono?' mono':'')+(options.long?' long':'');content.textContent=value===null||value===undefined||value===''?text('details.none'):String(value);item.append(name,content);return item}
20948
- function renderDetailHistory(runs){const section=document.createElement('section');section.className='detail-history';const title=document.createElement('h3');title.textContent=text('details.history');section.appendChild(title);if(!Array.isArray(runs)||runs.length===0){const empty=document.createElement('div');empty.className='muted small';empty.textContent=text('details.noHistory');section.appendChild(empty);return section}const list=document.createElement('div');list.className='detail-history-list';for(const run of runs){const item=document.createElement('div');item.className='detail-history-item';const primary=document.createElement('strong');primary.textContent='Run #'+run.id+' \xB7 '+detailStatus('run',run.status);const secondary=document.createElement('span');secondary.className='muted';secondary.textContent=detailDate(run.startedAt)+(run.model?' \xB7 '+detailModel(run.model):'');item.append(primary,secondary);list.appendChild(item)}section.appendChild(list);return section}
21072
+ function renderDetailHistory(runs){const section=document.createElement('section');section.className='detail-history';const title=document.createElement('h3');title.textContent=text('details.history');section.appendChild(title);if(!Array.isArray(runs)||runs.length===0){const empty=document.createElement('div');empty.className='muted small';empty.textContent=text('details.noHistory');section.appendChild(empty);return section}const list=document.createElement('div');list.className='detail-history-list';for(const run of runs){const item=document.createElement('div');item.className='detail-history-item';const primary=document.createElement('strong');primary.textContent='Run #'+run.id+' \xB7 '+detailStatus('run',run.status);const secondary=document.createElement('span');secondary.className='muted';secondary.textContent=detailDate(run.startedAt)+(run.model?' \xB7 '+detailModel(run.model):'')+(run.variant?' \xB7 '+run.variant:'');item.append(primary,secondary);list.appendChild(item)}section.appendChild(list);return section}
20949
21073
  function detailFields(type,data){if(type==='task')return [
20950
21074
  [text('details.id'),'#'+data.id],[text('table.name'),data.name],[text('table.status'),detailStatus('task',data.status)],[text('details.project'),data.cwd,{wide:true,mono:true}],
20951
- [text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.prompt'),data.prompt,{wide:true,long:true}],
21075
+ [text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.variant'),data.variant||text('details.default')],[text('details.prompt'),data.prompt,{wide:true,long:true}],
20952
21076
  [text('details.category'),data.category],[text('details.batch'),data.batchId],[text('details.dependency'),data.dependsOn?'#'+data.dependsOn:text('details.none')],
20953
21077
  [text('details.importance'),data.importance],[text('details.urgency'),data.urgency],[text('details.retryCount'),String(data.retryCount??0)+' / '+String(data.maxRetries??0)],
20954
21078
  [text('details.retryBackoff'),detailDuration(data.retryBackoffMs)],[text('details.timeout'),detailDuration(data.timeoutMs)],[text('details.scheduledAt'),detailDate(data.scheduledAt)],
20955
21079
  [text('details.createdAt'),detailDate(data.createdAt)],[text('details.startedAt'),detailDate(data.startedAt)],[text('details.finishedAt'),detailDate(data.finishedAt)],
20956
21080
  [text('details.result'),detailTaskResult(data),{wide:true,long:true}]
20957
21081
  ];if(type==='run'){const started=data.startedAt?new Date(data.startedAt).getTime():null;const finished=data.finishedAt?new Date(data.finishedAt).getTime():null;return [
20958
- [text('details.id'),'Run #'+data.id],[text('details.taskId'),'#'+data.taskId],[text('table.status'),detailStatus('run',data.status)],[text('table.model'),detailModel(data.model)],
21082
+ [text('details.id'),'Run #'+data.id],[text('details.taskId'),'#'+data.taskId],[text('table.status'),detailStatus('run',data.status)],[text('table.model'),detailModel(data.model)],[text('details.variant'),data.variant||text('details.default')],
20959
21083
  [text('details.session'),detailSession(data.sessionId)],[text('details.startedAt'),detailDate(data.startedAt)],[text('details.finishedAt'),detailDate(data.finishedAt)],
20960
21084
  [text('table.duration'),started!==null?detailDuration((finished??Date.now())-started):text('details.none')],[text('details.heartbeat'),detailDate(data.heartbeatAt)],
20961
21085
  [text('details.process'),'Worker PID '+String(data.workerPid??'\u2014')+' \xB7 OpenCode PID '+String(data.childPid??'\u2014'),{wide:true,mono:true}]
20962
21086
  ];}const scheduleRule=data.scheduleType==='cron'?data.cronExpr:data.scheduleType==='recurring'?detailDuration(data.intervalMs):detailDate(data.runAt);return [
20963
21087
  [text('details.id'),'#'+data.id],[text('table.name'),data.name],[text('details.enabled'),data.enabled?text('details.enabledYes'):text('details.enabledNo')],[text('details.project'),data.cwd,{wide:true,mono:true}],
20964
- [text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.prompt'),data.prompt,{wide:true,long:true}],
21088
+ [text('table.agent'),data.agent],[text('table.model'),detailModel(data.model)],[text('details.variant'),data.variant||text('details.default')],[text('details.prompt'),data.prompt,{wide:true,long:true}],
20965
21089
  [text('template.scheduleType'),detailScheduleType(data.scheduleType)],[text('details.scheduleRule'),scheduleRule],[text('details.category'),data.category],[text('details.batch'),data.batchId],
20966
21090
  [text('details.importance'),data.importance],[text('details.urgency'),data.urgency],[text('details.maxInstances'),data.maxInstances],[text('details.maxRetries'),data.maxRetries??0],
20967
21091
  [text('details.retryBackoff'),detailDuration(data.retryBackoffMs)],[text('details.timeout'),detailDuration(data.timeoutMs)],[text('details.lastRun'),detailDate(data.lastRunAt)],[text('details.nextRun'),detailDate(data.nextRunAt)],
@@ -20978,12 +21102,20 @@ function renderLayout(options) {
20978
21102
  function updateTaskProjectStatus(){const node=taskField('project-status');if(!node)return;const cwd=taskField('cwd').value.trim();if(!cwd){node.textContent='';return}const project=taskProjects()[cwd];node.textContent=project?text('task.projectExisting',project):text('task.projectNew')}
20979
21103
  const catalogTimers={};const catalogRequests={};const catalogModels={};
20980
21104
  function catalogField(prefix,name){return document.getElementById(prefix+'-'+name)}
20981
- function resetCatalog(prefix){const agent=catalogField(prefix,'agent');const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');if(!agent||!provider||!model)return;catalogModels[prefix]=[];agent.replaceChildren(new Option(text('catalog.chooseProject'),''));provider.replaceChildren(new Option(text('catalog.defaultProvider'),''));model.replaceChildren(new Option(text('catalog.defaultModel'),'default'));agent.disabled=false;provider.disabled=true;model.disabled=true;catalogField(prefix,'catalog-status').textContent='';}
21105
+ function resetCatalog(prefix){const agent=catalogField(prefix,'agent');const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');const variant=catalogField(prefix,'variant');if(!agent||!provider||!model||!variant)return;const preferredVariant=variant.dataset.preferred||'';catalogModels[prefix]=[];agent.replaceChildren(new Option(text('catalog.chooseProject'),''));provider.replaceChildren(new Option(text('catalog.defaultProvider'),''));model.replaceChildren(new Option(text('catalog.defaultModel'),'default'));variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));if(preferredVariant)appendCurrentOption(variant,preferredVariant);variant.value=preferredVariant;agent.disabled=false;provider.disabled=true;model.disabled=true;variant.disabled=!preferredVariant;catalogField(prefix,'catalog-status').textContent='';}
20982
21106
  function appendCurrentOption(select,value){if(!value||[...select.options].some(option=>option.value===value))return;select.appendChild(new Option(value,value));}
21107
+ function setPreferredVariant(prefix,modelValue,variantValue){const variant=catalogField(prefix,'variant');if(!variant)return;const preferred=variantValue||'';variant.dataset.preferred=preferred;variant.dataset.preferredModel=modelValue||'default';variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));if(preferred)appendCurrentOption(variant,preferred);variant.value=preferred;variant.disabled=!preferred}
21108
+ function invalidateVariantRequest(prefix){const key=prefix+'-variant';catalogRequests[key]=(catalogRequests[key]||0)+1}
21109
+ function invalidateCatalogRequests(prefix){clearTimeout(catalogTimers[prefix]);catalogRequests[prefix]=(catalogRequests[prefix]||0)+1;invalidateVariantRequest(prefix)}
21110
+ function clearVariantPreference(prefix){const variant=catalogField(prefix,'variant');if(!variant)return;invalidateVariantRequest(prefix);variant.dataset.preferred='';variant.dataset.preferredModel='';variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));variant.value='';variant.disabled=true}
21111
+ function handleProviderChange(prefix){clearVariantPreference(prefix);populateModelOptions(prefix)}
21112
+ function handleModelChange(prefix){clearVariantPreference(prefix);populateVariantOptions(prefix)}
21113
+ function handleVariantChange(prefix){const variant=catalogField(prefix,'variant');if(!variant)return;invalidateVariantRequest(prefix);variant.dataset.preferred='';variant.dataset.preferredModel=''}
20983
21114
  function modelProvider(value){const slash=value.indexOf('/');return slash>0?value.slice(0,slash):''}
20984
- function populateModelOptions(prefix,preferredModel=''){const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');if(!provider||!model)return;const selectedProvider=provider.value;model.replaceChildren();if(!selectedProvider){model.appendChild(new Option(text('catalog.defaultModel'),'default'));model.disabled=true;return}const available=(catalogModels[prefix]||[]).filter(value=>modelProvider(value)===selectedProvider);for(const value of available)model.appendChild(new Option(value,value));if(preferredModel&&available.includes(preferredModel))model.value=preferredModel;model.disabled=available.length===0}
21115
+ function populateModelOptions(prefix,preferredModel=''){const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');if(!provider||!model)return;const selectedProvider=provider.value;model.replaceChildren();if(!selectedProvider){model.appendChild(new Option(text('catalog.defaultModel'),'default'));model.disabled=true;populateVariantOptions(prefix);return}const available=(catalogModels[prefix]||[]).filter(value=>modelProvider(value)===selectedProvider);for(const value of available)model.appendChild(new Option(value,value));if(preferredModel&&available.includes(preferredModel))model.value=preferredModel;model.disabled=available.length===0;populateVariantOptions(prefix)}
21116
+ async function populateVariantOptions(prefix,preferredVariant=''){const cwd=catalogField(prefix,'cwd')?.value.trim()||'';const model=catalogField(prefix,'model');const variant=catalogField(prefix,'variant');if(!model||!variant)return;const selectedModel=model.value;const preferred=preferredVariant||(variant.dataset.preferredModel===selectedModel?variant.dataset.preferred||'':'');if(!cwd||!selectedModel||selectedModel==='default'){variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));if(preferred)appendCurrentOption(variant,preferred);variant.value=preferred;variant.dataset.preferred='';variant.dataset.preferredModel='';variant.disabled=!preferred;return}if(!preferred){variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));variant.value=''}const requestKey=prefix+'-variant';const request=(catalogRequests[requestKey]||0)+1;catalogRequests[requestKey]=request;variant.disabled=true;try{const data=await readJson(await fetch('/api/opencode/catalog?cwd='+encodeURIComponent(cwd)));if(catalogRequests[requestKey]!==request||catalogField(prefix,'cwd')?.value.trim()!==cwd||model.value!==selectedModel)return;const available=Array.isArray(data.variantsByModel?.[selectedModel])?data.variantsByModel[selectedModel]:[];variant.replaceChildren(new Option(text('catalog.defaultVariant'),''));for(const value of available)variant.appendChild(new Option(value,value));if(preferred)appendCurrentOption(variant,preferred);variant.value=[...variant.options].some(option=>option.value===preferred)?preferred:'';variant.dataset.preferred='';variant.dataset.preferredModel='';variant.disabled=available.length===0&&!preferred}catch(error){if(catalogRequests[requestKey]!==request||catalogField(prefix,'cwd')?.value.trim()!==cwd||model.value!==selectedModel)return;if(preferred)appendCurrentOption(variant,preferred);variant.value=preferred;variant.dataset.preferred=preferred;variant.dataset.preferredModel=selectedModel;variant.disabled=!preferred}}
20985
21117
  async function loadCatalog(prefix,preferredAgent='',preferredModel='default',preserveUnavailable=false){const cwd=catalogField(prefix,'cwd')?.value.trim()||'';const status=catalogField(prefix,'catalog-status');const agent=catalogField(prefix,'agent');const provider=catalogField(prefix,'model-provider');const model=catalogField(prefix,'model');if(!cwd||!status||!agent||!provider||!model){if(!cwd)resetCatalog(prefix);return}const request=(catalogRequests[prefix]||0)+1;catalogRequests[prefix]=request;status.dataset.state='loading';status.textContent=text('catalog.loading');agent.disabled=true;provider.disabled=true;model.disabled=true;try{const data=await readJson(await fetch('/api/opencode/catalog?cwd='+encodeURIComponent(cwd)));if(catalogRequests[prefix]!==request||catalogField(prefix,'cwd').value.trim()!==cwd)return;agent.replaceChildren();for(const item of data.agents){const label=item.name+' \u2014 '+text('catalog.'+item.mode);agent.appendChild(new Option(label,item.name))}if(preserveUnavailable)appendCurrentOption(agent,preferredAgent);const defaultAgent=preferredAgent||data.agents.find(item=>item.name==='build')?.name||data.agents.find(item=>item.mode==='primary')?.name||data.agents[0]?.name||'';agent.value=[...agent.options].some(option=>option.value===defaultAgent)?defaultAgent:(agent.options[0]?.value||'');catalogModels[prefix]=[...data.models];if(preserveUnavailable&&preferredModel&&preferredModel!=='default'&&!catalogModels[prefix].includes(preferredModel))catalogModels[prefix].push(preferredModel);provider.replaceChildren(new Option(text('catalog.defaultProvider'),''));for(const name of [...new Set(catalogModels[prefix].map(modelProvider).filter(Boolean))].sort())provider.appendChild(new Option(name,name));const preferredProvider=preferredModel==='default'?'':modelProvider(preferredModel);provider.value=[...provider.options].some(option=>option.value===preferredProvider)?preferredProvider:'';populateModelOptions(prefix,preferredModel);status.dataset.state='ready';status.textContent=text('catalog.loaded',{agents:data.agents.length,models:data.models.length})}catch(error){if(catalogRequests[prefix]!==request)return;resetCatalog(prefix);if(preserveUnavailable){appendCurrentOption(agent,preferredAgent);if(preferredModel&&preferredModel!=='default'){catalogModels[prefix]=[preferredModel];appendCurrentOption(provider,modelProvider(preferredModel));provider.value=modelProvider(preferredModel);populateModelOptions(prefix,preferredModel)}}status.dataset.state='error';status.textContent=text('catalog.failed',{error:error.message})}finally{if(catalogRequests[prefix]===request){agent.disabled=false;provider.disabled=false;if(provider.value)model.disabled=false}}}
20986
- function scheduleCatalogLoad(prefix){clearTimeout(catalogTimers[prefix]);catalogTimers[prefix]=setTimeout(()=>loadCatalog(prefix),450)}
21118
+ function scheduleCatalogLoad(prefix){invalidateCatalogRequests(prefix);resetCatalog(prefix);catalogTimers[prefix]=setTimeout(()=>loadCatalog(prefix),450)}
20987
21119
  let directoryTargetId='';let directoryCurrent='';let directoryEntries=[];let directoryShowHidden=false;
20988
21120
  function renderDirectoryEntries(){const list=document.getElementById('directory-list');list.replaceChildren();const entries=directoryEntries.filter(entry=>directoryShowHidden||!entry.hidden);const hidden=document.getElementById('directory-hidden');hidden.textContent=text(directoryShowHidden?'action.hideHidden':'action.showHidden');if(entries.length===0){const empty=document.createElement('div');empty.className='directory-empty';empty.textContent=text('directory.empty');list.appendChild(empty);return}for(const entry of entries){const button=document.createElement('button');button.type='button';button.className='directory-item';button.innerHTML='${icon("folder")}<span></span>';button.querySelector('span').textContent=entry.name;button.onclick=()=>browseDirectory(entry.path);list.appendChild(button)}}
20989
21121
  async function browseDirectory(path=''){const choose=document.getElementById('directory-choose');choose.disabled=true;try{const suffix=path?'?path='+encodeURIComponent(path):'';const data=await readJson(await fetch('/api/filesystem/directories'+suffix));directoryCurrent=data.path;directoryEntries=data.directories;document.getElementById('directory-path').textContent=data.path;document.getElementById('directory-up').disabled=data.parent===data.path;document.getElementById('directory-up').onclick=()=>browseDirectory(data.parent);document.getElementById('directory-home').onclick=()=>browseDirectory(data.home);renderDirectoryEntries();choose.disabled=false;return true}catch(error){directoryCurrent='';directoryEntries=[];document.getElementById('directory-path').textContent='';renderDirectoryEntries();showToast(error.message,'error');return false}}
@@ -20993,16 +21125,16 @@ function renderLayout(options) {
20993
21125
  function updateDurationControl(id){const preset=document.getElementById(id+'-preset');const custom=document.getElementById(id+'-custom');const input=document.getElementById(id+'-value');const visible=preset.value==='custom';custom.hidden=!visible;input.required=visible;if(visible&&!input.value)input.value='1'}
20994
21126
  function readDuration(id){const preset=document.getElementById(id+'-preset').value;if(preset==='')return '';if(preset!=='custom')return preset==='0'?'0':preset+'ms';const value=document.getElementById(id+'-value').value.trim();return value===''?'':value+document.getElementById(id+'-unit').value}
20995
21127
  function setDuration(id,milliseconds){const preset=document.getElementById(id+'-preset');const exact=milliseconds==null?'':String(milliseconds);if([...preset.options].some(option=>option.value===exact)){preset.value=exact;updateDurationControl(id);return}preset.value='custom';const input=document.getElementById(id+'-value');const unit=document.getElementById(id+'-unit');const units=[['d',86400000],['h',3600000],['min',60000],['s',1000]];let matched=false;for(const [name,factor] of units){if(milliseconds!=null&&(milliseconds===0||milliseconds%factor===0)){input.value=String(milliseconds/factor);unit.value=name;matched=true;break}}if(!matched){input.value=String((milliseconds??60000)/1000);unit.value='s'}updateDurationControl(id)}
20996
- function openTaskCreator(){const form=document.getElementById('task-form');form.reset();taskField('id').value='';taskField('cwd').readOnly=false;taskField('cwd-picker').hidden=false;taskField('cwd').value=form.dataset.defaultCwd||'';setDuration('task-retry-backoff',30000);setDuration('task-timeout',null);taskField('dialog-title').textContent=text('task.createTitle');taskField('save').textContent=text('action.saveTask');updateTaskProjectStatus();resetCatalog('task');document.getElementById('task-dialog').showModal();if(taskField('cwd').value)loadCatalog('task');setTimeout(()=>taskField('name').focus(),50)}
20997
- async function openTaskEditor(id){try{const data=await readJson(await fetch('/api/tasks/'+id));taskField('id').value=String(id);taskField('cwd').value=data.cwd||'';taskField('cwd').readOnly=true;taskField('cwd-picker').hidden=true;taskField('name').value=data.name||'';taskField('prompt').value=data.prompt||'';taskField('category').value=data.category||'general';taskField('batch').value=data.batchId||'';taskField('importance').value=String(data.importance??3);taskField('urgency').value=String(data.urgency??3);taskField('max-retries').value=String(data.maxRetries??3);setDuration('task-retry-backoff',data.retryBackoffMs??30000);setDuration('task-timeout',data.timeoutMs);taskField('dialog-title').textContent=text('task.editTitle');taskField('save').textContent=text('action.updateTask');updateTaskProjectStatus();resetCatalog('task');document.getElementById('task-dialog').showModal();loadCatalog('task',data.agent||'',data.model||'default',true);setTimeout(()=>taskField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
20998
- async function saveTask(event){event.preventDefault();const form=document.getElementById('task-form');if(!form.reportValidity())return;const id=taskField('id').value;const body={name:taskField('name').value,cwd:taskField('cwd').value,agent:taskField('agent').value,model:taskField('model').value,prompt:taskField('prompt').value,category:taskField('category').value,batchId:taskField('batch').value,importance:Number(taskField('importance').value),urgency:Number(taskField('urgency').value),maxRetries:Number(taskField('max-retries').value),retryBackoff:readDuration('task-retry-backoff'),timeout:readDuration('task-timeout')};const button=taskField('save');button.disabled=true;try{const data=await readJson(await fetch(id?'/api/tasks/'+id:'/api/tasks',{method:id?'PUT':'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));showToast(text(id?'feedback.taskUpdated':'feedback.taskCreated',{id:data.task.id}));document.getElementById('task-dialog').close();setTimeout(()=>location.assign(id?location.href:'/?cwd='+encodeURIComponent(data.task.cwd||'')),450)}catch(error){showToast(error.message,'error')}finally{button.disabled=false}}
21128
+ function openTaskCreator(){invalidateCatalogRequests('task');const form=document.getElementById('task-form');form.reset();taskField('id').value='';setPreferredVariant('task','default','');taskField('cwd').readOnly=false;taskField('cwd-picker').hidden=false;taskField('cwd').value=form.dataset.defaultCwd||'';setDuration('task-retry-backoff',30000);setDuration('task-timeout',null);taskField('dialog-title').textContent=text('task.createTitle');taskField('save').textContent=text('action.saveTask');updateTaskProjectStatus();resetCatalog('task');document.getElementById('task-dialog').showModal();if(taskField('cwd').value)loadCatalog('task');setTimeout(()=>taskField('name').focus(),50)}
21129
+ async function openTaskEditor(id){invalidateCatalogRequests('task');try{const data=await readJson(await fetch('/api/tasks/'+id));document.getElementById('task-form').reset();taskField('id').value=String(id);taskField('cwd').value=data.cwd||'';taskField('cwd').readOnly=true;taskField('cwd-picker').hidden=true;taskField('name').value=data.name||'';taskField('prompt').value=data.prompt||'';taskField('category').value=data.category||'general';taskField('batch').value=data.batchId||'';taskField('importance').value=String(data.importance??3);taskField('urgency').value=String(data.urgency??3);taskField('max-retries').value=String(data.maxRetries??3);setDuration('task-retry-backoff',data.retryBackoffMs??30000);setDuration('task-timeout',data.timeoutMs);taskField('dialog-title').textContent=text('task.editTitle');taskField('save').textContent=text('action.updateTask');updateTaskProjectStatus();setPreferredVariant('task',data.model||'default',data.variant||'');resetCatalog('task');document.getElementById('task-dialog').showModal();loadCatalog('task',data.agent||'',data.model||'default',true);setTimeout(()=>taskField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
21130
+ async function saveTask(event){event.preventDefault();const form=document.getElementById('task-form');if(!form.reportValidity())return;const id=taskField('id').value;const body={name:taskField('name').value,cwd:taskField('cwd').value,agent:taskField('agent').value,model:taskField('model').value,variant:taskField('variant').value,prompt:taskField('prompt').value,category:taskField('category').value,batchId:taskField('batch').value,importance:Number(taskField('importance').value),urgency:Number(taskField('urgency').value),maxRetries:Number(taskField('max-retries').value),retryBackoff:readDuration('task-retry-backoff'),timeout:readDuration('task-timeout')};const button=taskField('save');button.disabled=true;try{const data=await readJson(await fetch(id?'/api/tasks/'+id:'/api/tasks',{method:id?'PUT':'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));showToast(text(id?'feedback.taskUpdated':'feedback.taskCreated',{id:data.task.id}));document.getElementById('task-dialog').close();setTimeout(()=>location.assign(id?location.href:'/?cwd='+encodeURIComponent(data.task.cwd||'')),450)}catch(error){showToast(error.message,'error')}finally{button.disabled=false}}
20999
21131
  function localDateTime(milliseconds){const date=new Date(milliseconds);const local=new Date(milliseconds-date.getTimezoneOffset()*60000);return local.toISOString().slice(0,23)}
21000
21132
  function updateTemplateScheduleFields(){const type=templateField('schedule-type').value;const fields={cron:templateField('cron-field'),recurring:templateField('interval-field'),delayed:templateField('run-at-field')};for(const [name,node] of Object.entries(fields)){node.hidden=name!==type;for(const control of node.querySelectorAll('input,select'))control.required=false;const required=name==='recurring'?node.querySelector('[id$="-preset"]'):node.querySelector('input');if(required)required.required=name===type;if(name==='recurring'&&name===type)updateDurationControl('template-interval')}}
21001
21133
  function setOriginalRunAt(epoch){const input=templateField('run-at');const local=epoch?localDateTime(epoch):'';input.value=local;input.dataset.originalEpoch=epoch?String(epoch):'';input.dataset.originalLocal=local}
21002
21134
  function selectedRunAt(){const input=templateField('run-at');return resolveEditedRunAt(input.dataset.originalEpoch?Number(input.dataset.originalEpoch):null,input.dataset.originalLocal||'',input.value)}
21003
- function openTemplateCreator(){const form=document.getElementById('template-form');form.reset();templateField('id').value='';templateField('dialog-title').textContent=text('template.createTitle');setDuration('template-interval',3600000);setDuration('template-retry-backoff',30000);setDuration('template-timeout',null);setOriginalRunAt(null);templateField('run-at').value=localDateTime(Date.now()+3600000);updateTemplateScheduleFields();resetCatalog('template');document.getElementById('template-dialog').showModal();if(templateField('cwd').value)loadCatalog('template');setTimeout(()=>templateField('name').focus(),50)}
21004
- async function openTemplateEditor(id){try{const data=await readJson(await fetch('/api/templates/'+id));templateField('id').value=String(id);templateField('dialog-title').textContent=text('template.editTitle');templateField('name').value=data.name||'';templateField('cwd').value=data.cwd||'';templateField('prompt').value=data.prompt||'';templateField('schedule-type').value=data.scheduleType;templateField('cron').value=data.cronExpr||'';setDuration('template-interval',data.intervalMs);setOriginalRunAt(data.runAt||null);if(!data.runAt)templateField('run-at').value=localDateTime(Date.now()+3600000);templateField('category').value=data.category||'general';templateField('batch').value=data.batchId||'';templateField('importance').value=String(data.importance??3);templateField('urgency').value=String(data.urgency??3);templateField('max-instances').value=String(data.maxInstances??1);templateField('max-retries').value=String(data.maxRetries??3);setDuration('template-retry-backoff',data.retryBackoffMs??30000);setDuration('template-timeout',data.timeoutMs);updateTemplateScheduleFields();resetCatalog('template');document.getElementById('template-dialog').showModal();loadCatalog('template',data.agent||'',data.model||'default',true);setTimeout(()=>templateField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
21005
- async function saveTemplate(event){event.preventDefault();const form=document.getElementById('template-form');if(!form.reportValidity())return;const id=templateField('id').value;const type=templateField('schedule-type').value;const body={name:templateField('name').value,cwd:templateField('cwd').value,agent:templateField('agent').value,model:templateField('model').value,prompt:templateField('prompt').value,scheduleType:type,cronExpr:templateField('cron').value,interval:readDuration('template-interval'),runAt:type==='delayed'?selectedRunAt():null,category:templateField('category').value,batchId:templateField('batch').value,importance:Number(templateField('importance').value),urgency:Number(templateField('urgency').value),maxInstances:Number(templateField('max-instances').value),maxRetries:Number(templateField('max-retries').value),retryBackoff:readDuration('template-retry-backoff'),timeout:readDuration('template-timeout')};const button=templateField('save');button.disabled=true;try{await readJson(await fetch(id?'/api/templates/'+id:'/api/templates',{method:id?'PUT':'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));showToast(text(id?'feedback.templateUpdated':'feedback.templateCreated'));document.getElementById('template-dialog').close();setTimeout(()=>location.assign(id?location.href:'/templates'),450)}catch(error){showToast(error.message,'error')}finally{button.disabled=false}}
21135
+ function openTemplateCreator(){invalidateCatalogRequests('template');const form=document.getElementById('template-form');form.reset();templateField('id').value='';setPreferredVariant('template','default','');templateField('dialog-title').textContent=text('template.createTitle');setDuration('template-interval',3600000);setDuration('template-retry-backoff',30000);setDuration('template-timeout',null);setOriginalRunAt(null);templateField('run-at').value=localDateTime(Date.now()+3600000);updateTemplateScheduleFields();resetCatalog('template');document.getElementById('template-dialog').showModal();if(templateField('cwd').value)loadCatalog('template');setTimeout(()=>templateField('name').focus(),50)}
21136
+ async function openTemplateEditor(id){invalidateCatalogRequests('template');try{const data=await readJson(await fetch('/api/templates/'+id));document.getElementById('template-form').reset();templateField('id').value=String(id);templateField('dialog-title').textContent=text('template.editTitle');templateField('name').value=data.name||'';templateField('cwd').value=data.cwd||'';templateField('prompt').value=data.prompt||'';templateField('schedule-type').value=data.scheduleType;templateField('cron').value=data.cronExpr||'';setDuration('template-interval',data.intervalMs);setOriginalRunAt(data.runAt||null);if(!data.runAt)templateField('run-at').value=localDateTime(Date.now()+3600000);templateField('category').value=data.category||'general';templateField('batch').value=data.batchId||'';templateField('importance').value=String(data.importance??3);templateField('urgency').value=String(data.urgency??3);templateField('max-instances').value=String(data.maxInstances??1);templateField('max-retries').value=String(data.maxRetries??3);setDuration('template-retry-backoff',data.retryBackoffMs??30000);setDuration('template-timeout',data.timeoutMs);updateTemplateScheduleFields();setPreferredVariant('template',data.model||'default',data.variant||'');resetCatalog('template');document.getElementById('template-dialog').showModal();loadCatalog('template',data.agent||'',data.model||'default',true);setTimeout(()=>templateField('name').focus(),50)}catch(error){showToast(error.message,'error')}}
21137
+ async function saveTemplate(event){event.preventDefault();const form=document.getElementById('template-form');if(!form.reportValidity())return;const id=templateField('id').value;const type=templateField('schedule-type').value;const body={name:templateField('name').value,cwd:templateField('cwd').value,agent:templateField('agent').value,model:templateField('model').value,variant:templateField('variant').value,prompt:templateField('prompt').value,scheduleType:type,cronExpr:templateField('cron').value,interval:readDuration('template-interval'),runAt:type==='delayed'?selectedRunAt():null,category:templateField('category').value,batchId:templateField('batch').value,importance:Number(templateField('importance').value),urgency:Number(templateField('urgency').value),maxInstances:Number(templateField('max-instances').value),maxRetries:Number(templateField('max-retries').value),retryBackoff:readDuration('template-retry-backoff'),timeout:readDuration('template-timeout')};const button=templateField('save');button.disabled=true;try{await readJson(await fetch(id?'/api/templates/'+id:'/api/templates',{method:id?'PUT':'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)}));showToast(text(id?'feedback.templateUpdated':'feedback.templateCreated'));document.getElementById('template-dialog').close();setTimeout(()=>location.assign(id?location.href:'/templates'),450)}catch(error){showToast(error.message,'error')}finally{button.disabled=false}}
21006
21138
  async function enableTmpl(id){try{await readJson(await fetch('/api/templates/'+id+'/enable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
21007
21139
  async function disableTmpl(id){if(!await ask(text('dialog.disableTemplate'),text('dialog.disableTemplateBody')))return;try{await readJson(await fetch('/api/templates/'+id+'/disable',{method:'POST'}));location.reload()}catch(error){showToast(error.message,'error')}}
21008
21140
  async function deleteTmpl(id){if(!await ask(text('dialog.deleteTemplate'),text('dialog.deleteTemplateBody'),true))return;try{await readJson(await fetch('/api/templates/'+id,{method:'DELETE'}));location.reload()}catch(error){showToast(error.message,'error')}}
@@ -21184,6 +21316,7 @@ var init_ui = __esm({
21184
21316
  "template.cwdHint": "OpenCode \u4F1A\u5728\u8FD9\u4E2A\u76EE\u5F55\u4E2D\u6267\u884C\u4EFB\u52A1\u3002",
21185
21317
  "template.agent": "Agent",
21186
21318
  "template.model": "\u6A21\u578B",
21319
+ "template.variant": "\u6A21\u578B Variant",
21187
21320
  "template.prompt": "\u63D0\u793A\u8BCD",
21188
21321
  "template.scheduleType": "\u6267\u884C\u65B9\u5F0F",
21189
21322
  "template.cronExpr": "Cron \u8868\u8FBE\u5F0F",
@@ -21221,9 +21354,11 @@ var init_ui = __esm({
21221
21354
  "catalog.chooseProject": "\u8BF7\u5148\u9009\u62E9\u9879\u76EE\u76EE\u5F55",
21222
21355
  "catalog.defaultModel": "\u8DDF\u968F Agent / OpenCode \u9ED8\u8BA4\u6A21\u578B",
21223
21356
  "catalog.defaultProvider": "\u9ED8\u8BA4\u6A21\u578B",
21357
+ "catalog.defaultVariant": "\u8DDF\u968F Agent / \u6A21\u578B\u9ED8\u8BA4\u8BBE\u7F6E",
21224
21358
  "catalog.provider": "\u6A21\u578B\u63D0\u4F9B\u5546",
21225
21359
  "catalog.model": "\u5177\u4F53\u6A21\u578B",
21226
- "catalog.modelHint": "\u5148\u9009\u63D0\u4F9B\u5546\uFF0C\u518D\u9009\u672C\u9879\u76EE opencode models \u8FD4\u56DE\u7684\u6A21\u578B\uFF1B\u9ED8\u8BA4\u9009\u9879\u4E0D\u4F1A\u4F20\u5165 -m\u3002",
21360
+ "catalog.modelHint": "\u5148\u9009\u63D0\u4F9B\u5546\uFF0C\u518D\u9009\u672C\u9879\u76EE opencode models --verbose \u8FD4\u56DE\u7684\u6A21\u578B\uFF1B\u9ED8\u8BA4\u9009\u9879\u4E0D\u4F1A\u4F20\u5165 -m\u3002",
21361
+ "catalog.variantHint": "\u4EC5\u663E\u793A\u6240\u9009\u6A21\u578B\u58F0\u660E\u652F\u6301\u7684 variants\uFF1B\u9ED8\u8BA4\u9009\u9879\u4E0D\u4F1A\u4F20\u5165 --variant\u3002",
21227
21362
  "catalog.agentHint": "\u6765\u81EA\u5F53\u524D\u9879\u76EE\u7684 opencode agent list\u3002",
21228
21363
  "catalog.loading": "\u6B63\u5728\u8BFB\u53D6\u6B64\u9879\u76EE\u53EF\u7528\u7684 Agent \u548C\u6A21\u578B\u2026",
21229
21364
  "catalog.loaded": "\u5DF2\u4ECE\u672C\u673A OpenCode \u8BFB\u53D6 {agents} \u4E2A Agent\u3001{models} \u4E2A\u6A21\u578B\u3002",
@@ -21254,6 +21389,7 @@ var init_ui = __esm({
21254
21389
  "details.copySuccess": "\u539F\u59CB\u6570\u636E\u5DF2\u590D\u5236",
21255
21390
  "details.id": "\u7F16\u53F7",
21256
21391
  "details.project": "\u9879\u76EE\u76EE\u5F55",
21392
+ "details.variant": "\u6A21\u578B Variant",
21257
21393
  "details.prompt": "\u63D0\u793A\u8BCD",
21258
21394
  "details.result": "\u6267\u884C\u7ED3\u679C / \u5931\u8D25\u539F\u56E0",
21259
21395
  "details.category": "\u5206\u7C7B",
@@ -21485,6 +21621,7 @@ var init_ui = __esm({
21485
21621
  "template.cwdHint": "OpenCode runs the task in this directory.",
21486
21622
  "template.agent": "Agent",
21487
21623
  "template.model": "Model",
21624
+ "template.variant": "Model variant",
21488
21625
  "template.prompt": "Prompt",
21489
21626
  "template.scheduleType": "Schedule",
21490
21627
  "template.cronExpr": "Cron expression",
@@ -21522,9 +21659,11 @@ var init_ui = __esm({
21522
21659
  "catalog.chooseProject": "Choose a project directory first",
21523
21660
  "catalog.defaultModel": "Use the Agent / OpenCode default model",
21524
21661
  "catalog.defaultProvider": "Default model",
21662
+ "catalog.defaultVariant": "Use the Agent / model default",
21525
21663
  "catalog.provider": "Model provider",
21526
21664
  "catalog.model": "Model",
21527
- "catalog.modelHint": "Choose a provider, then a model returned by opencode models for this project. Default does not pass -m.",
21665
+ "catalog.modelHint": "Choose a provider, then a model returned by opencode models --verbose for this project. Default does not pass -m.",
21666
+ "catalog.variantHint": "Only variants declared by the selected model are shown. Default does not pass --variant.",
21528
21667
  "catalog.agentHint": "Loaded from opencode agent list for this project.",
21529
21668
  "catalog.loading": "Loading Agents and models available to this project\u2026",
21530
21669
  "catalog.loaded": "Loaded {agents} Agents and {models} models from local OpenCode.",
@@ -21555,6 +21694,7 @@ var init_ui = __esm({
21555
21694
  "details.copySuccess": "Raw data copied",
21556
21695
  "details.id": "ID",
21557
21696
  "details.project": "Project directory",
21697
+ "details.variant": "Model variant",
21558
21698
  "details.prompt": "Prompt",
21559
21699
  "details.result": "Result / failure reason",
21560
21700
  "details.category": "Category",
@@ -22117,6 +22257,7 @@ function parseTaskPayload(value) {
22117
22257
  cwd: requiredString("cwd"),
22118
22258
  agent: requiredString("agent"),
22119
22259
  model: optionalString("model", "default"),
22260
+ variant: Object.hasOwn(input, "variant") ? optionalString("variant") : void 0,
22120
22261
  prompt: requiredString("prompt"),
22121
22262
  category: optionalString("category", "general"),
22122
22263
  batchId: optionalString("batchId"),
@@ -22132,6 +22273,7 @@ function editableTaskPayload(input) {
22132
22273
  name: input.name,
22133
22274
  agent: input.agent,
22134
22275
  model: input.model ?? "default",
22276
+ ...input.variant === void 0 ? {} : { variant: input.variant },
22135
22277
  prompt: input.prompt,
22136
22278
  category: input.category ?? "general",
22137
22279
  batchId: input.batchId ?? null,
@@ -22187,6 +22329,7 @@ function parseTemplatePayload(value) {
22187
22329
  name: requiredString("name"),
22188
22330
  agent: requiredString("agent"),
22189
22331
  model: optionalString("model", "default"),
22332
+ variant: Object.hasOwn(input, "variant") ? optionalString("variant") : void 0,
22190
22333
  prompt: requiredString("prompt"),
22191
22334
  cwd: requiredString("cwd"),
22192
22335
  category: optionalString("category", "general"),
@@ -22520,7 +22663,7 @@ var init_web = __esm({
22520
22663
  const latestRun = latestRuns.get(task.id);
22521
22664
  const executionActive = latestRun?.status === "running";
22522
22665
  const batchId = task.batchId?.trim() || null;
22523
- const searchable = esc(`${task.name} ${task.agent} ${task.prompt} ${task.cwd ?? ""} ${task.batchId ?? ""} ${task.category ?? ""}`);
22666
+ const searchable = esc(`${task.name} ${task.agent} ${task.model ?? ""} ${task.variant ?? ""} ${task.prompt} ${task.cwd ?? ""} ${task.batchId ?? ""} ${task.category ?? ""}`);
22524
22667
  return `<tr data-task-row data-search="${searchable}">
22525
22668
  <td class="faint" data-label="${t(locale, "table.id")}">#${task.id}</td>
22526
22669
  <td data-primary data-label="${t(locale, "table.task")}"><div class="task-name">${esc(task.name)}</div><div class="task-prompt" title="${esc(task.prompt)}">${esc(task.prompt.substring(0, 160))}</div>
@@ -22598,11 +22741,12 @@ var init_web = __esm({
22598
22741
  <label class="form-field"><span>${t(locale, "template.cwd")}</span><div class="field-action"><input id="task-cwd" required autocomplete="off" list="task-cwd-options" placeholder="/path/to/project" oninput="updateTaskProjectStatus();scheduleCatalogLoad('task')"><button id="task-cwd-picker" type="button" class="btn" onclick="openDirectoryPicker('task-cwd')">${icon("folder")}${t(locale, "action.chooseFolder")}</button></div><small>${t(locale, "template.cwdHint")}</small></label>
22599
22742
  <datalist id="task-cwd-options">${projects.map((project) => `<option value="${esc(project.cwd)}"></option>`).join("")}</datalist>
22600
22743
  <label class="form-field"><span>${t(locale, "template.agent")}</span><select id="task-agent" required><option value="">${t(locale, "catalog.chooseProject")}</option></select><small>${t(locale, "catalog.agentHint")}</small></label>
22601
- <label class="form-field"><span>${t(locale, "template.model")}</span><div class="model-selector"><select id="task-model-provider" aria-label="${t(locale, "catalog.provider")}" onchange="populateModelOptions('task')"><option value="">${t(locale, "catalog.defaultProvider")}</option></select><select id="task-model" required aria-label="${t(locale, "catalog.model")}" disabled><option value="default">${t(locale, "catalog.defaultModel")}</option></select></div><small>${t(locale, "catalog.modelHint")}</small></label>
22744
+ <label class="form-field"><span>${t(locale, "template.model")}</span><div class="model-selector"><select id="task-model-provider" aria-label="${t(locale, "catalog.provider")}" onchange="handleProviderChange('task')"><option value="">${t(locale, "catalog.defaultProvider")}</option></select><select id="task-model" required aria-label="${t(locale, "catalog.model")}" onchange="handleModelChange('task')" disabled><option value="default">${t(locale, "catalog.defaultModel")}</option></select></div><small>${t(locale, "catalog.modelHint")}</small></label>
22745
+ <label class="form-field"><span>${t(locale, "template.variant")}</span><select id="task-variant" onchange="handleVariantChange('task')" disabled><option value="">${t(locale, "catalog.defaultVariant")}</option></select><small>${t(locale, "catalog.variantHint")}</small></label>
22602
22746
  <label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="task-prompt" rows="6" required></textarea></label>
22603
22747
  </div>
22604
22748
  <p id="task-project-status" class="form-note"></p>
22605
- <p id="task-catalog-status" class="form-note catalog-status"></p>
22749
+ <p id="task-catalog-status" class="form-note catalog-status" role="status" aria-live="polite"></p>
22606
22750
  <details class="advanced-fields">
22607
22751
  <summary>${t(locale, "template.advanced")}</summary>
22608
22752
  <div class="template-form-grid">
@@ -22644,7 +22788,7 @@ var init_web = __esm({
22644
22788
  return `<tr>
22645
22789
  <td class="faint" data-label="${t(locale, "table.id")}">#${template.id}</td>
22646
22790
  <td data-primary data-label="${t(locale, "table.name")}"><div class="task-name">${esc(template.name)}</div><div class="task-prompt" title="${esc(template.prompt)}">${esc(template.prompt.substring(0, 140))}</div>
22647
- <div class="actions" style="margin-top:5px"><span class="tag">${esc(template.agent)}</span>${template.model && template.model !== "default" ? `<span class="tag">${esc(template.model)}</span>` : ""}</div></td>
22791
+ <div class="actions" style="margin-top:5px"><span class="tag">${esc(template.agent)}</span>${template.model && template.model !== "default" ? `<span class="tag">${esc(template.model)}</span>` : ""}${template.variant ? `<span class="tag">${esc(template.variant)}</span>` : ""}</div></td>
22648
22792
  <td data-label="${t(locale, "table.type")}"><span class="tag t-${scheduleType}">${typeLabel}</span></td>
22649
22793
  <td data-label="${t(locale, "table.rule")}" class="m small">${esc(rule)}</td>
22650
22794
  <td data-label="${t(locale, "table.status")}"><span class="badge ${template.enabled ? "b-done" : "b-cancelled"}">${t(locale, template.enabled ? "schedule.enabled" : "schedule.disabled")}</span></td>
@@ -22674,14 +22818,15 @@ var init_web = __esm({
22674
22818
  <label class="form-field"><span>${t(locale, "template.name")}</span><input id="template-name" required maxlength="200" autocomplete="off"></label>
22675
22819
  <label class="form-field"><span>${t(locale, "template.cwd")}</span><div class="field-action"><input id="template-cwd" required autocomplete="off" placeholder="/path/to/project" oninput="scheduleCatalogLoad('template')"><button type="button" class="btn" onclick="openDirectoryPicker('template-cwd')">${icon("folder")}${t(locale, "action.chooseFolder")}</button></div><small>${t(locale, "template.cwdHint")}</small></label>
22676
22820
  <label class="form-field"><span>${t(locale, "template.agent")}</span><select id="template-agent" required><option value="">${t(locale, "catalog.chooseProject")}</option></select><small>${t(locale, "catalog.agentHint")}</small></label>
22677
- <label class="form-field"><span>${t(locale, "template.model")}</span><div class="model-selector"><select id="template-model-provider" aria-label="${t(locale, "catalog.provider")}" onchange="populateModelOptions('template')"><option value="">${t(locale, "catalog.defaultProvider")}</option></select><select id="template-model" required aria-label="${t(locale, "catalog.model")}" disabled><option value="default">${t(locale, "catalog.defaultModel")}</option></select></div><small>${t(locale, "catalog.modelHint")}</small></label>
22821
+ <label class="form-field"><span>${t(locale, "template.model")}</span><div class="model-selector"><select id="template-model-provider" aria-label="${t(locale, "catalog.provider")}" onchange="handleProviderChange('template')"><option value="">${t(locale, "catalog.defaultProvider")}</option></select><select id="template-model" required aria-label="${t(locale, "catalog.model")}" onchange="handleModelChange('template')" disabled><option value="default">${t(locale, "catalog.defaultModel")}</option></select></div><small>${t(locale, "catalog.modelHint")}</small></label>
22822
+ <label class="form-field"><span>${t(locale, "template.variant")}</span><select id="template-variant" onchange="handleVariantChange('template')" disabled><option value="">${t(locale, "catalog.defaultVariant")}</option></select><small>${t(locale, "catalog.variantHint")}</small></label>
22678
22823
  <label class="form-field form-field-wide"><span>${t(locale, "template.prompt")}</span><textarea id="template-prompt" rows="6" required></textarea></label>
22679
22824
  <label class="form-field"><span>${t(locale, "template.scheduleType")}</span><select id="template-schedule-type" onchange="updateTemplateScheduleFields()"><option value="recurring">${t(locale, "schedule.recurring")}</option><option value="delayed">${t(locale, "schedule.delayed")}</option><option value="cron">${t(locale, "schedule.cron")}</option></select></label>
22680
22825
  <label id="template-cron-field" class="form-field" hidden><span>${t(locale, "template.cronExpr")}</span><input id="template-cron" autocomplete="off" placeholder="0 9 * * *"><small>${t(locale, "template.cronHint")}</small></label>
22681
22826
  <label id="template-interval-field" class="form-field"><span>${t(locale, "template.interval")}</span>${durationControl(locale, "template-interval", 36e5, "interval")}<small>${t(locale, "template.intervalHint")}</small></label>
22682
22827
  <label id="template-run-at-field" class="form-field" hidden><span>${t(locale, "template.runAt")}</span><input id="template-run-at" type="datetime-local" step="0.001"></label>
22683
22828
  </div>
22684
- <p id="template-catalog-status" class="form-note catalog-status"></p>
22829
+ <p id="template-catalog-status" class="form-note catalog-status" role="status" aria-live="polite"></p>
22685
22830
  <details class="advanced-fields">
22686
22831
  <summary>${t(locale, "template.advanced")}</summary>
22687
22832
  <div class="template-form-grid">
@@ -22715,6 +22860,7 @@ var init_web = __esm({
22715
22860
  taskId: taskRuns4.taskId,
22716
22861
  sessionId: taskRuns4.sessionId,
22717
22862
  model: taskRuns4.model,
22863
+ variant: taskRuns4.variant,
22718
22864
  status: taskRuns4.status,
22719
22865
  startedAt: taskRuns4.startedAt,
22720
22866
  finishedAt: taskRuns4.finishedAt,
@@ -22735,7 +22881,7 @@ var init_web = __esm({
22735
22881
  const log = run.log ? renderRunLog(run.id, run.taskName, run.log, locale, run.status !== "done") : "";
22736
22882
  return `<tr class="run-summary-row">
22737
22883
  <td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td>
22738
- <td data-primary data-label="${t(locale, "table.task")}"><div class="task-name">${esc(run.taskName)} <span class="faint">#${run.taskId}</span></div>${run.model ? `<div style="margin-top:4px"><span class="tag">${esc(run.model)}</span></div>` : ""}</td>
22884
+ <td data-primary data-label="${t(locale, "table.task")}"><div class="task-name">${esc(run.taskName)} <span class="faint">#${run.taskId}</span></div>${run.model || run.variant ? `<div class="actions" style="margin-top:4px">${run.model ? `<span class="tag">${esc(run.model)}</span>` : ""}${run.variant ? `<span class="tag">${esc(run.variant)}</span>` : ""}</div>` : ""}</td>
22739
22885
  <td data-label="${t(locale, "table.agent")}"><span class="tag">${esc(run.taskAgent)}</span></td>
22740
22886
  <td data-label="${t(locale, "table.session")}" class="m small">${esc(maskSessionId(run.sessionId))}</td>
22741
22887
  <td data-label="${t(locale, "table.status")}"><span class="badge b-${status}">${runStatusText(locale, run.status ?? "unknown")}</span></td>
@@ -22785,7 +22931,7 @@ var init_web = __esm({
22785
22931
  const runRows = runningRuns.map((run) => {
22786
22932
  const session = maskSessionId(run.sessionId);
22787
22933
  return `<tr><td class="faint" data-label="${t(locale, "table.run")}">#${run.id}</td><td data-primary data-label="${t(locale, "table.task")}">#${run.taskId}</td><td data-label="${t(locale, "table.session")}" class="m small">${esc(session)}</td>
22788
- <td data-label="${t(locale, "table.model")}" class="small">${esc(run.model) || "\u2014"}</td><td data-label="${t(locale, "table.startedAt")}" class="small">${formatDateTime(run.startedAt, locale)}</td>
22934
+ <td data-label="${t(locale, "table.model")}" class="small">${esc(run.model) || "\u2014"}${run.variant ? ` \xB7 ${esc(run.variant)}` : ""}</td><td data-label="${t(locale, "table.startedAt")}" class="small">${formatDateTime(run.startedAt, locale)}</td>
22789
22935
  <td data-label="${t(locale, "table.heartbeat")}" class="small muted">${formatRelative(run.heartbeatAt, locale)}</td><td data-label="${t(locale, "table.pid")}" class="m small">W:${run.workerPid ?? "\u2014"} C:${run.childPid ?? "\u2014"}</td>
22790
22936
  <td data-label="${t(locale, "table.duration")}" class="small">${formatDuration(run.startedAt, null)}</td></tr>`;
22791
22937
  }).join("");
@@ -23211,6 +23357,7 @@ var WorkerEngine = class {
23211
23357
  const run = await TaskRunService.create({
23212
23358
  taskId: task.id,
23213
23359
  model: this.resolveModel(task.model),
23360
+ variant: this.resolveVariant(task.variant),
23214
23361
  status: "running",
23215
23362
  workerPid: process.pid,
23216
23363
  lockedAt: Date.now(),
@@ -23273,8 +23420,10 @@ var WorkerEngine = class {
23273
23420
  }
23274
23421
  async spawnTask(task, runId, launchIdentity) {
23275
23422
  const model = this.resolveModel(task.model);
23423
+ const variant = this.resolveVariant(task.variant);
23276
23424
  const args = ["run", "--agent", task.agent, "--format", "json"];
23277
23425
  if (model) args.push("-m", model);
23426
+ if (variant) args.push("--variant", variant);
23278
23427
  args.push(task.prompt);
23279
23428
  const cwd = task.cwd || process.cwd();
23280
23429
  const child = spawn(process.execPath, [
@@ -23433,7 +23582,7 @@ var WorkerEngine = class {
23433
23582
  );
23434
23583
  return;
23435
23584
  }
23436
- const failure = code === 0 ? void 0 : `${spawnError ? "\u65E0\u6CD5\u542F\u52A8 opencode" : "opencode \u9000\u51FA\u7801"} ${spawnError?.message ?? code ?? "null"}${signal ? `\uFF0C\u4FE1\u53F7 ${signal}` : ""}\uFF08agent=${entry.task.agent}\uFF0Cmodel=${this.resolveModel(entry.task.model) ?? "Agent/\u9ED8\u8BA4\u914D\u7F6E"}\uFF0Ccwd=${entry.task.cwd ?? process.cwd()}\uFF09`;
23585
+ const failure = code === 0 ? void 0 : `${spawnError ? "\u65E0\u6CD5\u542F\u52A8 opencode" : "opencode \u9000\u51FA\u7801"} ${spawnError?.message ?? code ?? "null"}${signal ? `\uFF0C\u4FE1\u53F7 ${signal}` : ""}\uFF08agent=${entry.task.agent}\uFF0Cmodel=${this.resolveModel(entry.task.model) ?? "Agent/\u9ED8\u8BA4\u914D\u7F6E"}\uFF0Cvariant=${this.resolveVariant(entry.task.variant) ?? "Agent/\u6A21\u578B\u9ED8\u8BA4\u914D\u7F6E"}\uFF0Ccwd=${entry.task.cwd ?? process.cwd()}\uFF09`;
23437
23586
  this.runDetached(
23438
23587
  this.settleEntry(entry, code, failure),
23439
23588
  "task settlement failed",
@@ -23622,6 +23771,9 @@ ${output}` : ""}`;
23622
23771
  if (!taskModel || taskModel === "default") return null;
23623
23772
  return taskModel;
23624
23773
  }
23774
+ resolveVariant(taskVariant) {
23775
+ return taskVariant?.trim() || null;
23776
+ }
23625
23777
  runDetached(operation, message, taskId) {
23626
23778
  operation.catch((error) => {
23627
23779
  markGatewayFailure("worker", error);