opencode-acp 1.14.25-pr.350.73 → 1.14.25-pr.351.82

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -324,11 +324,12 @@ Each level overrides the previous, so project settings take priority over global
324
324
  // Soft upper threshold: above this, ACP keeps injecting strong
325
325
  // compression nudges (based on nudgeFrequency), so compression is
326
326
  // much more likely. Accepts: number or "X%" of model context window.
327
- "maxContextLimit": "55%",
328
- // Soft lower threshold for reminder nudges: below this, turn/iteration
329
- // reminders are off (compression less likely). At/above this, reminders
330
- // are on. Accepts: number or "X%" of model context window.
331
- "minContextLimit": "45%",
327
+ "maxContextLimit": "80%",
328
+ // Soft lower threshold for turn/iteration reminder nudges: below this,
329
+ // those reminders are off (compression less likely). At/above this, they
330
+ // are on. Growth nudges have their own floor: minNudgeContextPercent.
331
+ // Accepts: number or "X%" of model context window.
332
+ "minContextLimit": "80%",
332
333
  // Optional per-model override for maxContextLimit by providerID/modelID.
333
334
  // If present, this wins over the global maxContextLimit.
334
335
  // Accepts: number or "X%".
package/dist/index.js CHANGED
@@ -888,6 +888,7 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
888
888
  "compress.minContextLimit",
889
889
  "compress.modelMaxLimits",
890
890
  "compress.modelMinLimits",
891
+ "compress.providers",
891
892
  "compress.nudgeFrequency",
892
893
  "compress.minNudgeContextPercent",
893
894
  "compress.nudgeGrowthTokens",
@@ -931,7 +932,7 @@ function getConfigKeyPaths(obj, prefix = "") {
931
932
  for (const key of Object.keys(obj)) {
932
933
  const fullKey = prefix ? `${prefix}.${key}` : key;
933
934
  keys.push(fullKey);
934
- if (fullKey === "compress.modelMaxLimits" || fullKey === "compress.modelMinLimits" || fullKey === "messageFilters.filters") {
935
+ if (fullKey === "compress.modelMaxLimits" || fullKey === "compress.modelMinLimits" || fullKey === "compress.providers" || fullKey === "messageFilters.filters") {
935
936
  continue;
936
937
  }
937
938
  if (obj[key] && typeof obj[key] === "object" && !Array.isArray(obj[key])) {
@@ -1311,6 +1312,85 @@ function validateConfigTypes(config) {
1311
1312
  }
1312
1313
  validateModelLimits("compress.modelMaxLimits", compress.modelMaxLimits);
1313
1314
  validateModelLimits("compress.modelMinLimits", compress.modelMinLimits);
1315
+ const validatePercentField = (key, value) => {
1316
+ if (value === void 0) {
1317
+ return;
1318
+ }
1319
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 100) {
1320
+ errors.push({
1321
+ key,
1322
+ expected: "number (0-100)",
1323
+ actual: JSON.stringify(value)
1324
+ });
1325
+ }
1326
+ };
1327
+ const validateModelOverrides = (prefix, overrides) => {
1328
+ if (overrides === void 0) {
1329
+ return;
1330
+ }
1331
+ if (typeof overrides !== "object" || overrides === null || Array.isArray(overrides)) {
1332
+ errors.push({ key: prefix, expected: "CompressModelOverrides", actual: typeof overrides });
1333
+ return;
1334
+ }
1335
+ const model = overrides;
1336
+ for (const field of Object.keys(model)) {
1337
+ if (field !== "minNudgeContextPercent") {
1338
+ errors.push({
1339
+ key: `${prefix}.${field}`,
1340
+ expected: "known override field (minNudgeContextPercent)",
1341
+ actual: "unknown field"
1342
+ });
1343
+ }
1344
+ }
1345
+ validatePercentField(`${prefix}.minNudgeContextPercent`, model.minNudgeContextPercent);
1346
+ };
1347
+ const validateProviderOverrides = (providers) => {
1348
+ if (providers === void 0) {
1349
+ return;
1350
+ }
1351
+ if (typeof providers !== "object" || providers === null || Array.isArray(providers)) {
1352
+ errors.push({
1353
+ key: "compress.providers",
1354
+ expected: "Record<string, ProviderOverrides>",
1355
+ actual: typeof providers
1356
+ });
1357
+ return;
1358
+ }
1359
+ for (const [providerId, providerValue] of Object.entries(providers)) {
1360
+ const prefix = `compress.providers.${providerId}`;
1361
+ if (typeof providerValue !== "object" || providerValue === null || Array.isArray(providerValue)) {
1362
+ errors.push({ key: prefix, expected: "ProviderOverrides", actual: typeof providerValue });
1363
+ continue;
1364
+ }
1365
+ const provider = providerValue;
1366
+ for (const field of Object.keys(provider)) {
1367
+ if (field !== "minNudgeContextPercent" && field !== "models") {
1368
+ errors.push({
1369
+ key: `${prefix}.${field}`,
1370
+ expected: "known override field (minNudgeContextPercent, models)",
1371
+ actual: "unknown field"
1372
+ });
1373
+ }
1374
+ }
1375
+ validatePercentField(`${prefix}.minNudgeContextPercent`, provider.minNudgeContextPercent);
1376
+ const models = provider.models;
1377
+ if (models === void 0) {
1378
+ continue;
1379
+ }
1380
+ if (typeof models !== "object" || models === null || Array.isArray(models)) {
1381
+ errors.push({
1382
+ key: `${prefix}.models`,
1383
+ expected: "Record<string, ModelOverrides>",
1384
+ actual: typeof models
1385
+ });
1386
+ continue;
1387
+ }
1388
+ for (const [modelId, modelValue] of Object.entries(models)) {
1389
+ validateModelOverrides(`${prefix}.models.${modelId}`, modelValue);
1390
+ }
1391
+ }
1392
+ };
1393
+ validateProviderOverrides(compress.providers);
1314
1394
  const validValues = ["ask", "allow", "deny"];
1315
1395
  if (compress.permission !== void 0 && !validValues.includes(compress.permission)) {
1316
1396
  errors.push({
@@ -1489,7 +1569,7 @@ var defaultConfig = {
1489
1569
  maxContextLimit: "80%",
1490
1570
  minContextLimit: "80%",
1491
1571
  nudgeFrequency: 5,
1492
- minNudgeContextPercent: 15,
1572
+ minNudgeContextPercent: 5,
1493
1573
  iterationNudgeThreshold: 15,
1494
1574
  nudgeForce: "soft",
1495
1575
  protectedTools: [...COMPRESS_DEFAULT_PROTECTED_TOOLS],
@@ -1611,6 +1691,39 @@ function loadConfigFile(configPath) {
1611
1691
  return { data: null, parseError: error.message || "Failed to parse config" };
1612
1692
  }
1613
1693
  }
1694
+ function stripUndefined(value) {
1695
+ const result = {};
1696
+ for (const [key, entry] of Object.entries(value)) {
1697
+ if (entry !== void 0) {
1698
+ result[key] = entry;
1699
+ }
1700
+ }
1701
+ return result;
1702
+ }
1703
+ function mergeModelOverrides(base, override) {
1704
+ if (base === void 0) return override;
1705
+ if (override === void 0) return base;
1706
+ const merged = { ...base };
1707
+ for (const [modelId, model] of Object.entries(override)) {
1708
+ merged[modelId] = { ...base[modelId], ...stripUndefined(model) };
1709
+ }
1710
+ return merged;
1711
+ }
1712
+ function mergeProviderOverrides(base, override) {
1713
+ if (base === void 0) return override;
1714
+ if (override === void 0) return base;
1715
+ const merged = { ...base };
1716
+ for (const [providerId, provider] of Object.entries(override)) {
1717
+ const baseProvider = base[providerId];
1718
+ const mergedModels = mergeModelOverrides(baseProvider?.models, provider.models);
1719
+ merged[providerId] = {
1720
+ ...baseProvider,
1721
+ ...stripUndefined(provider),
1722
+ ...mergedModels !== void 0 ? { models: mergedModels } : {}
1723
+ };
1724
+ }
1725
+ return merged;
1726
+ }
1614
1727
  function mergeCompress(base, override) {
1615
1728
  if (!override) {
1616
1729
  return base;
@@ -1623,6 +1736,7 @@ function mergeCompress(base, override) {
1623
1736
  minContextLimit: override.minContextLimit ?? base.minContextLimit,
1624
1737
  modelMaxLimits: override.modelMaxLimits ?? base.modelMaxLimits,
1625
1738
  modelMinLimits: override.modelMinLimits ?? base.modelMinLimits,
1739
+ providers: mergeProviderOverrides(base.providers, override.providers),
1626
1740
  nudgeFrequency: override.nudgeFrequency ?? base.nudgeFrequency,
1627
1741
  minNudgeContextPercent: override.minNudgeContextPercent ?? base.minNudgeContextPercent,
1628
1742
  nudgeGrowthTokens: override.nudgeGrowthTokens,
@@ -1642,8 +1756,7 @@ function mergeCompress(base, override) {
1642
1756
  lastSegmentSoftBlock: override.lastSegmentSoftBlock ?? base.lastSegmentSoftBlock,
1643
1757
  preserveRecentMessages: override.preserveRecentMessages ?? base.preserveRecentMessages,
1644
1758
  preserveRecentTokens: override.preserveRecentTokens ?? base.preserveRecentTokens,
1645
- preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage,
1646
- completionReserveTokens: override.completionReserveTokens ?? base.completionReserveTokens
1759
+ preserveLastUserMessage: override.preserveLastUserMessage ?? base.preserveLastUserMessage
1647
1760
  };
1648
1761
  }
1649
1762
  function mergeCommands(base, override) {
@@ -1674,6 +1787,22 @@ function deepCloneConfig(config) {
1674
1787
  ...config.compress,
1675
1788
  modelMaxLimits: { ...config.compress.modelMaxLimits },
1676
1789
  modelMinLimits: { ...config.compress.modelMinLimits },
1790
+ providers: config.compress.providers ? Object.fromEntries(
1791
+ Object.entries(config.compress.providers).map(([providerId, provider]) => [
1792
+ providerId,
1793
+ {
1794
+ ...provider,
1795
+ ...provider.models ? {
1796
+ models: Object.fromEntries(
1797
+ Object.entries(provider.models).map(([modelId, model]) => [
1798
+ modelId,
1799
+ { ...model }
1800
+ ])
1801
+ )
1802
+ } : {}
1803
+ }
1804
+ ])
1805
+ ) : void 0,
1677
1806
  protectedTools: [...config.compress.protectedTools]
1678
1807
  },
1679
1808
  gc: {
@@ -4302,8 +4431,7 @@ function createSessionState() {
4302
4431
  modelProviderID: void 0,
4303
4432
  modelID: void 0,
4304
4433
  systemPromptTokens: void 0,
4305
- qualityGateRetryPending: false,
4306
- noContextLimitWarned: false
4434
+ qualityGateRetryPending: false
4307
4435
  };
4308
4436
  }
4309
4437
  function resetSessionState(state) {
@@ -4345,7 +4473,6 @@ function resetSessionState(state) {
4345
4473
  state.modelID = void 0;
4346
4474
  state.systemPromptTokens = void 0;
4347
4475
  state.qualityGateRetryPending = false;
4348
- state.noContextLimitWarned = false;
4349
4476
  }
4350
4477
  async function ensureSessionInitialized(client, state, sessionId, logger, messages, config) {
4351
4478
  if (state.sessionId === sessionId) {
@@ -6960,6 +7087,26 @@ function computeShouldNudge2(params) {
6960
7087
  return policy.computeShouldNudge(params);
6961
7088
  }
6962
7089
  var DEFAULT_NUDGE_GROWTH_TOKENS = 5e4;
7090
+ var DEFAULT_MIN_NUDGE_CONTEXT_PERCENT = 5;
7091
+ function resolveMinNudgeContextPercent(config, providerId, modelId) {
7092
+ const modelOverrides = providerId !== void 0 && modelId !== void 0 ? config.compress?.providers?.[providerId]?.models?.[modelId] : void 0;
7093
+ if (modelOverrides?.minNudgeContextPercent !== void 0) {
7094
+ return modelOverrides.minNudgeContextPercent;
7095
+ }
7096
+ const providerOverrides = providerId !== void 0 ? config.compress?.providers?.[providerId] : void 0;
7097
+ if (providerOverrides?.minNudgeContextPercent !== void 0) {
7098
+ return providerOverrides.minNudgeContextPercent;
7099
+ }
7100
+ return config.compress?.minNudgeContextPercent;
7101
+ }
7102
+ function resolveMinNudgeFloorTokens(config, modelContextLimit, providerId, modelId) {
7103
+ if (modelContextLimit === void 0) {
7104
+ return void 0;
7105
+ }
7106
+ const percent = resolveMinNudgeContextPercent(config, providerId, modelId) ?? DEFAULT_MIN_NUDGE_CONTEXT_PERCENT;
7107
+ const clamped = Math.min(100, Math.max(0, percent));
7108
+ return Math.round(clamped / 100 * modelContextLimit);
7109
+ }
6963
7110
  function addAnchor(anchorMessageIds, anchorMessageId, anchorMessageIndex, messages, interval) {
6964
7111
  if (anchorMessageIndex < 0) {
6965
7112
  return false;
@@ -7642,11 +7789,18 @@ var injectCompressNudges = (state, config, logger, messages, prompts, compressio
7642
7789
  overMinLimit,
7643
7790
  overMaxLimit,
7644
7791
  lastNudgeTokens: growthReference,
7645
- minNudgeContextPercent: config.compress?.minNudgeContextPercent ?? 15,
7792
+ minNudgeContextPercent: resolveMinNudgeContextPercent(config, providerId, modelId) ?? DEFAULT_MIN_NUDGE_CONTEXT_PERCENT,
7646
7793
  nudgeGrowthTokens: effectiveThreshold
7647
7794
  });
7648
7795
  const growthSinceBaseline = currentTokens !== void 0 && growthReference !== void 0 ? currentTokens - growthReference : void 0;
7649
- const nudgeAllowed = emergencyOverride || decision.shouldNudge && growthSinceBaseline !== void 0 && growthSinceBaseline >= growthFloor;
7796
+ const minNudgeFloorTokens = resolveMinNudgeFloorTokens(
7797
+ config,
7798
+ modelContextLimit,
7799
+ providerId,
7800
+ modelId
7801
+ );
7802
+ const overMinNudgeFloor = minNudgeFloorTokens === void 0 || currentTokens === void 0 || currentTokens >= minNudgeFloorTokens;
7803
+ const nudgeAllowed = emergencyOverride || decision.shouldNudge && (overMaxLimit || overMinNudgeFloor) && growthSinceBaseline !== void 0 && growthSinceBaseline >= growthFloor;
7650
7804
  const effectiveTipsVariant = emergencyOverride ? "maxLimit" : decision.tipsVariant;
7651
7805
  if (state.nudges.lastPerMessageNudgeTokens === void 0 && currentTokens !== void 0) {
7652
7806
  state.nudges.lastPerMessageNudgeTokens = currentTokens;
@@ -9113,7 +9267,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
9113
9267
  import { join as join3 } from "path";
9114
9268
  import { existsSync as existsSync3 } from "fs";
9115
9269
  import { homedir as homedir3 } from "os";
9116
- var LOG_VERSION = true ? "1.14.25-pr.350.73" : "dev";
9270
+ var LOG_VERSION = true ? "1.14.25-pr.351.82" : "dev";
9117
9271
  var LEVEL_RANK = {
9118
9272
  debug: 10,
9119
9273
  info: 20,
@@ -9998,148 +10152,6 @@ function truncateLargeToolOutputs(state, config, logger, messages) {
9998
10152
  }
9999
10153
  }
10000
10154
 
10001
- // lib/messages/enforce-budget.ts
10002
- var DEFAULT_COMPLETION_RESERVE_TOKENS = 32768;
10003
- var TRUNCATION_MARKER2 = "[truncated for context space";
10004
- var KEEP_PREFIX_CHARS2 = 2e3;
10005
- var KEEP_SUFFIX_CHARS2 = 2e3;
10006
- var PROTECT_RECENT_MESSAGES2 = 3;
10007
- var MIN_CLEAR_TOKENS = 200;
10008
- function resolveContextWindow(state) {
10009
- if (typeof state.modelContextLimit === "number" && state.modelContextLimit > 0) {
10010
- return state.modelContextLimit;
10011
- }
10012
- return void 0;
10013
- }
10014
- function estimateWireTokens(state, messages) {
10015
- const base = getCurrentTokenUsage(state, messages);
10016
- if (base > 0) {
10017
- let lastAssistant = -1;
10018
- for (let i = messages.length - 1; i >= 0; i--) {
10019
- if (messages[i].info.role === "assistant") {
10020
- lastAssistant = i;
10021
- break;
10022
- }
10023
- }
10024
- let additions = 0;
10025
- for (let i = lastAssistant + 1; i < messages.length; i++) {
10026
- additions += countAllMessageTokens(messages[i]);
10027
- }
10028
- return base + additions;
10029
- }
10030
- let total = 0;
10031
- for (const m of messages) total += countAllMessageTokens(m);
10032
- return total + (state.systemPromptTokens ?? 0);
10033
- }
10034
- function enforceContextBudget(state, config, logger, messages) {
10035
- const window = resolveContextWindow(state);
10036
- if (window === void 0) return void 0;
10037
- const reserve = config.compress?.completionReserveTokens ?? DEFAULT_COMPLETION_RESERVE_TOKENS;
10038
- const budget = window - reserve;
10039
- if (budget <= 0) return void 0;
10040
- const estimatedTokens = estimateWireTokens(state, messages);
10041
- if (estimatedTokens <= budget) {
10042
- return {
10043
- applied: false,
10044
- window,
10045
- reserve,
10046
- budget,
10047
- estimatedTokens,
10048
- finalEstimate: estimatedTokens,
10049
- truncatedCount: 0,
10050
- clearedCount: 0
10051
- };
10052
- }
10053
- const protectedIndex = messages.length - PROTECT_RECENT_MESSAGES2;
10054
- const protectedTools = new Set(config.compress?.protectedTools ?? []);
10055
- const candidates = [];
10056
- for (let mi = 0; mi < protectedIndex; mi++) {
10057
- if (mi === 0 && messages[mi].info.role === "user") continue;
10058
- const msg = messages[mi];
10059
- const parts = Array.isArray(msg.parts) ? msg.parts : [];
10060
- for (const part of parts) {
10061
- if (part?.type !== "tool") continue;
10062
- if (part.state?.status !== "completed") continue;
10063
- if (part.tool === "compress") continue;
10064
- if (protectedTools.has(part.tool)) continue;
10065
- const content = extractCompletedToolOutput(part);
10066
- if (content === void 0) continue;
10067
- if (content === COMPACTED_TOOL_OUTPUT_PLACEHOLDER) continue;
10068
- const tokens = countTokens2(content);
10069
- if (tokens <= 0) continue;
10070
- candidates.push({ part, content, tokens, index: mi });
10071
- }
10072
- }
10073
- let saved = 0;
10074
- let truncatedCount = 0;
10075
- let clearedCount = 0;
10076
- const truncatable = candidates.filter(
10077
- (c) => !c.content.includes(TRUNCATION_MARKER2) && c.content.length > KEEP_PREFIX_CHARS2 + KEEP_SUFFIX_CHARS2
10078
- ).sort((a, b) => b.tokens - a.tokens);
10079
- for (const c of truncatable) {
10080
- if (estimatedTokens - saved <= budget) break;
10081
- const prefix = c.content.slice(0, KEEP_PREFIX_CHARS2);
10082
- const suffix = c.content.slice(-KEEP_SUFFIX_CHARS2);
10083
- const truncated = prefix + `
10084
-
10085
- ...${TRUNCATION_MARKER2} \u2014 original ~${c.tokens} tokens]...
10086
-
10087
- ` + suffix;
10088
- c.part.state.output = truncated;
10089
- saved += c.tokens - countTokens2(truncated);
10090
- truncatedCount++;
10091
- }
10092
- if (estimatedTokens - saved > budget) {
10093
- const clearable = candidates.filter((c) => {
10094
- const out = extractCompletedToolOutput(c.part);
10095
- return out !== void 0 && out !== COMPACTED_TOOL_OUTPUT_PLACEHOLDER && countTokens2(out) > MIN_CLEAR_TOKENS;
10096
- }).sort((a, b) => a.index - b.index);
10097
- for (const c of clearable) {
10098
- if (estimatedTokens - saved <= budget) break;
10099
- const current = extractCompletedToolOutput(c.part);
10100
- if (current === void 0 || current === COMPACTED_TOOL_OUTPUT_PLACEHOLDER) continue;
10101
- c.part.state.output = COMPACTED_TOOL_OUTPUT_PLACEHOLDER;
10102
- saved += countTokens2(current) - countTokens2(COMPACTED_TOOL_OUTPUT_PLACEHOLDER);
10103
- clearedCount++;
10104
- }
10105
- }
10106
- const finalEstimate = Math.max(0, estimatedTokens - saved);
10107
- if (truncatedCount > 0 || clearedCount > 0) {
10108
- logger.warn("Context budget guard: pruned tool outputs to fit the request", {
10109
- session: state.sessionId,
10110
- estimatedTokens: Math.round(estimatedTokens),
10111
- budget,
10112
- window,
10113
- reserve,
10114
- truncatedCount,
10115
- clearedCount,
10116
- estimatedSavedTokens: Math.round(saved),
10117
- finalEstimate: Math.round(finalEstimate)
10118
- });
10119
- }
10120
- if (finalEstimate > budget) {
10121
- logger.warn(
10122
- "Context budget guard: still over budget after pruning all compressible tool outputs; the request may be rejected by the model",
10123
- {
10124
- session: state.sessionId,
10125
- finalEstimate: Math.round(finalEstimate),
10126
- budget,
10127
- window
10128
- }
10129
- );
10130
- }
10131
- return {
10132
- applied: truncatedCount > 0 || clearedCount > 0,
10133
- window,
10134
- reserve,
10135
- budget,
10136
- estimatedTokens,
10137
- finalEstimate,
10138
- truncatedCount,
10139
- clearedCount
10140
- };
10141
- }
10142
-
10143
10155
  // lib/commands/context.ts
10144
10156
  function analyzeTokens(state, messages) {
10145
10157
  const breakdown = {
@@ -11241,16 +11253,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11241
11253
  });
11242
11254
  }
11243
11255
  await updatePerTurnState(state, logger, messages);
11244
- if (state.modelContextLimit === void 0 && !state.noContextLimitWarned && requestModel?.providerID && requestModel?.modelID && registry4.resolveModelLimit(requestModel.providerID, requestModel.modelID) === void 0) {
11245
- state.noContextLimitWarned = true;
11246
- logger.warn(
11247
- 'Model reports no context window and the catalog has no entry for it; all percentage thresholds (min/max/emergency, GC) and the context-budget guard are disabled. Set the model limit in opencode.json (e.g. "limit": {"context": 262144, "output": 16384}) or set an absolute compress.maxContextLimit in acp.jsonc.',
11248
- {
11249
- session: state.sessionId,
11250
- model: `${requestModel.providerID}/${requestModel.modelID}`
11251
- }
11252
- );
11253
- }
11254
11256
  }
11255
11257
  syncCompressPermissionState(state, config, hostPermissions, output.messages);
11256
11258
  if (state.isSubAgent && !config.allowSubAgents) {
@@ -11281,7 +11283,6 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11281
11283
  const prePruneTokens = getCurrentTokenUsage(state, output.messages);
11282
11284
  prune(state, logger, config, output.messages);
11283
11285
  truncateLargeToolOutputs(state, config, logger, output.messages);
11284
- enforceContextBudget(state, config, logger, output.messages);
11285
11286
  hideConsumedCompressCalls(state, output.messages);
11286
11287
  assignMessageRefs(state, output.messages);
11287
11288
  const compressionPriorities = buildPriorityMap(config, state, output.messages);
@@ -11707,7 +11708,7 @@ var server = (async (ctx) => {
11707
11708
  }
11708
11709
  const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
11709
11710
  logger.info("ACP plugin initialized", {
11710
- version: true ? "1.14.25-pr.350.73" : "dev",
11711
+ version: true ? "1.14.25-pr.351.82" : "dev",
11711
11712
  workspace: ctx.directory,
11712
11713
  logLevel: logger.level,
11713
11714
  debug: config.debug,