opencode-acp 1.14.23-pr.331.51 → 1.14.23-pr.332.52

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
@@ -282,8 +282,11 @@ Each level overrides the previous, so project settings take priority over global
282
282
  // on the installed dist-tag/spec (@stable follows stable, @latest follows latest).
283
283
  // Version-locked plugin specs are not updated.
284
284
  "autoUpdate": true,
285
- // Enable INFO/DEBUG logging + per-request snapshots to ~/.config/opencode/logs/acp/
286
- // (WARN/ERROR are always logged to daily/<date>.log)
285
+ // File log verbosity: "debug" | "info" | "warn" | "error" | "silent".
286
+ // Default "info" writes decision-level events (nudges, transforms, updates)
287
+ // to ~/.config/opencode/logs/acp/daily/<date>.log
288
+ "logLevel": "info",
289
+ // Enable full DEBUG logging + per-request snapshots (overrides logLevel)
287
290
  "debug": false,
288
291
  // Notification display: "off", "minimal", or "detailed"
289
292
  "pruneNotification": "off",
package/README.zh-CN.md CHANGED
@@ -236,8 +236,11 @@ ACP 使用自己的配置文件,按以下顺序搜索:
236
236
  // 自动更新 npm 安装的 ACP:跟踪安装所用 dist-tag/规范(@stable 跟随 stable,@latest 跟随 latest)。
237
237
  // 版本锁定的规范不会被更新。
238
238
  "autoUpdate": true,
239
- // Enable INFO/DEBUG logging + per-request snapshots to ~/.config/opencode/logs/acp/
240
- // (WARN/ERROR are always logged to daily/<date>.log)
239
+ // 文件日志级别: "debug" | "info" | "warn" | "error" | "silent"
240
+ // 默认 "info":决策级事件(压缩提示、转换摘要、更新检查)落盘到
241
+ // ~/.config/opencode/logs/acp/daily/<日期>.log
242
+ "logLevel": "info",
243
+ // 启用完整 DEBUG 日志 + 按请求快照(优先于 logLevel)
241
244
  "debug": false,
242
245
  // Notification display: "off", "minimal", or "detailed"
243
246
  "pruneNotification": "detailed",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AA2BjD,QAAA,MAAM,MAAM,EAAE,MA6IK,CAAA;AAEnB,eAAe,MAAM,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AA2BjD,QAAA,MAAM,MAAM,EAAE,MAqJK,CAAA;AAEnB,eAAe,MAAM,CAAA"}
package/dist/index.js CHANGED
@@ -868,6 +868,7 @@ var VALID_CONFIG_KEYS = /* @__PURE__ */ new Set([
868
868
  "enabled",
869
869
  "autoUpdate",
870
870
  "debug",
871
+ "logLevel",
871
872
  "showUpdateToasts",
872
873
  "allowSubAgents",
873
874
  "pruneNotification",
@@ -1466,6 +1467,7 @@ var defaultConfig = {
1466
1467
  enabled: true,
1467
1468
  autoUpdate: true,
1468
1469
  debug: false,
1470
+ logLevel: "info",
1469
1471
  allowSubAgents: true,
1470
1472
  pruneNotification: "off",
1471
1473
  // [FIX #20] Default to toast — chat-mode notifications inject an empty
@@ -1727,6 +1729,7 @@ function mergeLayer(config, data) {
1727
1729
  enabled: data.enabled ?? config.enabled,
1728
1730
  autoUpdate: data.autoUpdate ?? config.autoUpdate,
1729
1731
  debug: data.debug ?? config.debug,
1732
+ logLevel: data.logLevel ?? config.logLevel,
1730
1733
  allowSubAgents: data.allowSubAgents ?? data.experimental?.allowSubAgents ?? config.allowSubAgents,
1731
1734
  pruneNotification: data.pruneNotification ?? config.pruneNotification,
1732
1735
  pruneNotificationType: data.pruneNotificationType ?? config.pruneNotificationType,
@@ -7757,6 +7760,13 @@ Multiple entries create separate blocks: \`content: [{ startId: "b${firstBlock.b
7757
7760
  ${rules}`;
7758
7761
  appendToLastTextPart(suffixMessage, tierText);
7759
7762
  shouldInject = true;
7763
+ logger.info(`Tier ${tc.triggerTier} trigger nudge injected`, {
7764
+ session: state.sessionId,
7765
+ action,
7766
+ targetTier: tc.targetTier,
7767
+ blocks: candidates.length,
7768
+ candidateTokens
7769
+ });
7760
7770
  if (tc.triggerTier === 2) {
7761
7771
  state.nudges.lastTier2NudgeTokens = currentTokens;
7762
7772
  } else {
@@ -7766,6 +7776,32 @@ ${rules}`;
7766
7776
  }
7767
7777
  }
7768
7778
  state.nudges.shouldInjectThisTurn = shouldInject;
7779
+ const usagePct = currentTokens !== void 0 && modelContextLimit ? `${(currentTokens / modelContextLimit * 100).toFixed(1)}%` : void 0;
7780
+ if (shouldInjectNudge) {
7781
+ logger.info("Compression nudge injected", {
7782
+ session: state.sessionId,
7783
+ trigger: emergencyOverride ? "emergency" : "growth",
7784
+ currentTokens,
7785
+ usagePct,
7786
+ growthSinceBaseline,
7787
+ growthFloor,
7788
+ recommendedRanges: recommendedRanges.length
7789
+ });
7790
+ } else if (shouldInjectNotice) {
7791
+ logger.info("Emergency /compact notice injected (no compressible targets)", {
7792
+ session: state.sessionId,
7793
+ currentTokens,
7794
+ usagePct
7795
+ });
7796
+ } else if (nudgeAllowed && nothingToCompress) {
7797
+ logger.info("Nudge suppressed: nothing to compress", {
7798
+ session: state.sessionId,
7799
+ reason: allProtected ? "all_protected" : allInProtectedZone ? "in_protected_zone" : "below_effective_floor",
7800
+ currentTokens,
7801
+ usagePct,
7802
+ compressibleRanges: contextRanges.compressible.length
7803
+ });
7804
+ }
7769
7805
  if (shouldInject && config.debug && contextRanges.compressible.length > 0) {
7770
7806
  const compressible = contextRanges.compressible;
7771
7807
  const fmt = (n) => n >= 1e3 ? `${(n / 1e3).toFixed(1)}K` : String(n);
@@ -9074,15 +9110,35 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
9074
9110
  import { join as join3 } from "path";
9075
9111
  import { existsSync as existsSync3 } from "fs";
9076
9112
  import { homedir as homedir3 } from "os";
9077
- var LOG_VERSION = true ? "1.14.23-pr.331.51" : "dev";
9113
+ var LOG_VERSION = true ? "1.14.23-pr.332.52" : "dev";
9114
+ var LEVEL_RANK = {
9115
+ debug: 10,
9116
+ info: 20,
9117
+ warn: 30,
9118
+ error: 40,
9119
+ silent: 99
9120
+ };
9078
9121
  var Logger = class {
9079
9122
  logDir;
9080
- enabled;
9081
- constructor(enabled) {
9082
- this.enabled = enabled;
9123
+ /** Resolved verbosity; replaces the old boolean `enabled` flag. */
9124
+ level;
9125
+ /**
9126
+ * @param enabled legacy boolean toggle — `true` maps to `debug`,
9127
+ * `false` maps to `warn` (errors + warnings only).
9128
+ * @param level explicit verbosity; when given it wins over `enabled`.
9129
+ */
9130
+ constructor(enabled, level) {
9131
+ this.level = level ?? (enabled ? "debug" : "warn");
9083
9132
  const configHome = process.env.XDG_CONFIG_HOME || join3(homedir3(), ".config");
9084
9133
  this.logDir = join3(configHome, "opencode", "logs", "acp");
9085
9134
  }
9135
+ /** Back-compat view of the old flag: true only at full debug verbosity. */
9136
+ get enabled() {
9137
+ return this.level === "debug";
9138
+ }
9139
+ shouldWrite(level) {
9140
+ return LEVEL_RANK[level] >= LEVEL_RANK[this.level];
9141
+ }
9086
9142
  async ensureLogDir() {
9087
9143
  if (!existsSync3(this.logDir)) {
9088
9144
  await mkdir2(this.logDir, { recursive: true });
@@ -9129,7 +9185,7 @@ var Logger = class {
9129
9185
  }
9130
9186
  }
9131
9187
  async write(level, component, message, data) {
9132
- if (!this.enabled && level !== "ERROR" && level !== "WARN") return;
9188
+ if (!this.shouldWrite(level.toLowerCase())) return;
9133
9189
  try {
9134
9190
  await this.ensureLogDir();
9135
9191
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -9146,12 +9202,10 @@ var Logger = class {
9146
9202
  }
9147
9203
  }
9148
9204
  info(message, data) {
9149
- if (!this.enabled) return;
9150
9205
  const component = this.getCallerFile(2);
9151
9206
  return this.write("INFO", component, message, data);
9152
9207
  }
9153
9208
  debug(message, data) {
9154
- if (!this.enabled) return;
9155
9209
  const component = this.getCallerFile(2);
9156
9210
  return this.write("DEBUG", component, message, data);
9157
9211
  }
@@ -9249,7 +9303,7 @@ var Logger = class {
9249
9303
  });
9250
9304
  }
9251
9305
  async saveContext(sessionId, messages) {
9252
- if (!this.enabled) return;
9306
+ if (this.level !== "debug") return;
9253
9307
  try {
9254
9308
  const contextDir = join3(this.logDir, "context", sessionId);
9255
9309
  if (!existsSync3(contextDir)) {
@@ -11010,6 +11064,7 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11010
11064
  requestModel?.providerID,
11011
11065
  requestModel?.modelID
11012
11066
  );
11067
+ const prevModelID = state.modelID;
11013
11068
  if (requestModelLimit !== void 0) {
11014
11069
  state.modelContextLimit = requestModelLimit;
11015
11070
  state.modelProviderID = requestModel?.providerID;
@@ -11029,6 +11084,14 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
11029
11084
  state.modelProviderID = requestModel.providerID;
11030
11085
  state.modelID = requestModel.modelID;
11031
11086
  }
11087
+ if (requestModel?.modelID && requestModel.modelID !== prevModelID) {
11088
+ logger.info("Model switched mid-session", {
11089
+ session: state.sessionId,
11090
+ from: prevModelID,
11091
+ to: requestModel.modelID,
11092
+ contextLimit: state.modelContextLimit
11093
+ });
11094
+ }
11032
11095
  await updatePerTurnState(state, logger, messages);
11033
11096
  }
11034
11097
  syncCompressPermissionState(state, config, hostPermissions, output.messages);
@@ -11090,6 +11153,17 @@ ${text}`);
11090
11153
  hideFailedCompressCalls(output.messages);
11091
11154
  stripStaleMetadata(output.messages);
11092
11155
  dropEmptyMessages(output.messages);
11156
+ const postTokens = getCurrentTokenUsage(state, output.messages);
11157
+ logger.info("Chat transform complete", {
11158
+ session: state.sessionId,
11159
+ model: state.modelID,
11160
+ messages: output.messages.length,
11161
+ prePruneTokens,
11162
+ postTokens,
11163
+ contextLimit: state.modelContextLimit,
11164
+ usagePct: postTokens !== void 0 && state.modelContextLimit ? `${(postTokens / state.modelContextLimit * 100).toFixed(1)}%` : void 0,
11165
+ nudged: state.nudges.shouldInjectThisTurn
11166
+ });
11093
11167
  if (state.sessionId) {
11094
11168
  await logger.saveContext(state.sessionId, output.messages);
11095
11169
  }
@@ -11267,12 +11341,31 @@ import { readFile as readFile2, rm } from "fs/promises";
11267
11341
  import { basename, dirname as dirname4, join as join6 } from "path";
11268
11342
  import { fileURLToPath } from "url";
11269
11343
  var PACKAGE_NAME = "opencode-acp";
11270
- function startAutoUpdate(ctx, enabled) {
11271
- if (!enabled) return;
11344
+ function startAutoUpdate(ctx, enabled, logger) {
11345
+ if (!enabled) {
11346
+ logger?.info("Auto-update disabled by config");
11347
+ return;
11348
+ }
11349
+ logger?.info("Auto-update check starting");
11272
11350
  const controller = new AbortController();
11273
11351
  const timeout = setTimeout(() => controller.abort(), 1e4);
11274
- void checkAutoUpdate(controller.signal).then((result) => {
11275
- if (!result.updated) return;
11352
+ void checkAutoUpdate(controller.signal, logger).then((result) => {
11353
+ if (!result.updated) {
11354
+ if ("error" in result) {
11355
+ logger?.warn("Auto-update failed", {
11356
+ error: result.error,
11357
+ name: result.name,
11358
+ current: result.current,
11359
+ latest: result.latest
11360
+ });
11361
+ }
11362
+ return;
11363
+ }
11364
+ logger?.info("Auto-update applied update", {
11365
+ name: result.name,
11366
+ from: result.current,
11367
+ to: result.latest
11368
+ });
11276
11369
  setTimeout(() => {
11277
11370
  ctx.client.tui.showToast({
11278
11371
  body: {
@@ -11286,17 +11379,37 @@ function startAutoUpdate(ctx, enabled) {
11286
11379
  }).catch(() => {
11287
11380
  }).finally(() => clearTimeout(timeout));
11288
11381
  }
11289
- async function checkAutoUpdate(signal) {
11382
+ async function checkAutoUpdate(signal, logger) {
11290
11383
  const packageDir = await findPackageDir(PACKAGE_NAME);
11291
- if (!packageDir) return { updated: false };
11384
+ if (!packageDir) {
11385
+ logger?.info("Auto-update skipped: package dir not found");
11386
+ return { updated: false };
11387
+ }
11292
11388
  const pkg = await readPackageJson(join6(packageDir, "package.json"));
11293
- if (!pkg?.name || !pkg.version) return { updated: false };
11389
+ if (!pkg?.name || !pkg.version) {
11390
+ logger?.info("Auto-update skipped: package.json unreadable", { packageDir });
11391
+ return { updated: false };
11392
+ }
11294
11393
  const target = await updateTarget(packageDir, pkg.name);
11295
- if (!target) return { updated: false };
11394
+ if (!target) {
11395
+ logger?.info("Auto-update skipped: no update target", { name: pkg.name, version: pkg.version });
11396
+ return { updated: false };
11397
+ }
11296
11398
  const tag = specUpdateTag(target.spec);
11297
- if (!tag) return { updated: false };
11399
+ if (!tag) {
11400
+ logger?.info("Auto-update skipped: spec not auto-updatable", { spec: target.spec });
11401
+ return { updated: false };
11402
+ }
11298
11403
  const latest = await fetchLatestVersion(pkg.name, tag, signal);
11299
- if (!latest || !isVersionNewer(latest, pkg.version)) return { updated: false };
11404
+ if (!latest) {
11405
+ logger?.info("Auto-update skipped: no version published for tag", { name: pkg.name, tag });
11406
+ return { updated: false };
11407
+ }
11408
+ if (!isVersionNewer(latest, pkg.version)) {
11409
+ logger?.info("Auto-update: already up to date", { name: pkg.name, tag, current: pkg.version, latest });
11410
+ return { updated: false };
11411
+ }
11412
+ logger?.info("Auto-update: newer version available", { name: pkg.name, tag, current: pkg.version, latest });
11300
11413
  try {
11301
11414
  await rm(target.removeDir, { recursive: true, force: true });
11302
11415
  } catch {
@@ -11429,7 +11542,15 @@ var server = (async (ctx) => {
11429
11542
  if (!config.enabled) {
11430
11543
  return {};
11431
11544
  }
11432
- const logger = new Logger(config.debug);
11545
+ const logger = new Logger(config.debug, config.debug ? "debug" : config.logLevel);
11546
+ logger.info("ACP plugin initialized", {
11547
+ version: true ? "1.14.23-pr.332.52" : "dev",
11548
+ workspace: ctx.directory,
11549
+ logLevel: logger.level,
11550
+ debug: config.debug,
11551
+ autoUpdate: config.autoUpdate,
11552
+ secureMode: isSecureMode()
11553
+ });
11433
11554
  const registry4 = new SessionStateRegistry(logger);
11434
11555
  const prompts = new PromptStore(logger, ctx.directory, config.experimental.customPrompts);
11435
11556
  const hostPermissions = {
@@ -11459,7 +11580,7 @@ var server = (async (ctx) => {
11459
11580
  }
11460
11581
  );
11461
11582
  logger.info("DCP initialized");
11462
- startAutoUpdate(ctx, config.autoUpdate);
11583
+ startAutoUpdate(ctx, config.autoUpdate, logger);
11463
11584
  const compressToolContext = {
11464
11585
  client: ctx.client,
11465
11586
  registry: registry4,