opencode-acp 1.14.19-pr.313.25 → 1.14.19-pr.315.27

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.
@@ -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,MAiHK,CAAA;AAEnB,eAAe,MAAM,CAAA"}
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"}
package/dist/index.js CHANGED
@@ -2084,18 +2084,6 @@ import { homedir as homedir2 } from "os";
2084
2084
  import { join as join2 } from "path";
2085
2085
 
2086
2086
  // lib/state/utils.ts
2087
- function syncModelIdentity(state, providerID, modelID) {
2088
- if (providerID === void 0 || modelID === void 0) {
2089
- return false;
2090
- }
2091
- if (state.modelProviderID === providerID && state.modelID === modelID) {
2092
- return false;
2093
- }
2094
- state.modelContextLimit = void 0;
2095
- state.modelProviderID = providerID;
2096
- state.modelID = modelID;
2097
- return true;
2098
- }
2099
2087
  var isMessageCompacted = (state, msg) => {
2100
2088
  if (!isMessageWithInfo(msg)) {
2101
2089
  return false;
@@ -2963,6 +2951,51 @@ function applyPendingCompressionDurations(state) {
2963
2951
  return updates;
2964
2952
  }
2965
2953
 
2954
+ // lib/state/model-limits.ts
2955
+ function createModelLimitCatalog() {
2956
+ const modelLimits = /* @__PURE__ */ new Map();
2957
+ return {
2958
+ record(providerId, modelId, limit) {
2959
+ if (!providerId || !modelId || typeof limit !== "number" || limit <= 0) return;
2960
+ modelLimits.set(`${providerId}/${modelId}`, limit);
2961
+ },
2962
+ resolve(providerId, modelId) {
2963
+ if (!providerId || !modelId) return void 0;
2964
+ return modelLimits.get(`${providerId}/${modelId}`);
2965
+ },
2966
+ /**
2967
+ * Best-effort one-time seed from the host's provider catalog
2968
+ * (`client.config.providers()` → GET /config/providers). Never throws;
2969
+ * returns the number of model-limit entries recorded.
2970
+ */
2971
+ async hydrateFromClient(client) {
2972
+ try {
2973
+ const config = client;
2974
+ const result = await config.config?.providers?.();
2975
+ const payload = result;
2976
+ const providers = payload?.data?.providers;
2977
+ if (!Array.isArray(providers)) return 0;
2978
+ let recorded = 0;
2979
+ for (const provider of providers) {
2980
+ const { id, models } = provider ?? {};
2981
+ if (typeof id !== "string" || !models) continue;
2982
+ for (const [modelId, model] of Object.entries(models)) {
2983
+ const limit = model?.limit;
2984
+ const context = limit?.context;
2985
+ if (typeof context === "number" && context > 0) {
2986
+ modelLimits.set(`${id}/${modelId}`, context);
2987
+ recorded++;
2988
+ }
2989
+ }
2990
+ }
2991
+ return recorded;
2992
+ } catch {
2993
+ return 0;
2994
+ }
2995
+ }
2996
+ };
2997
+ }
2998
+
2966
2999
  // lib/compress/search.ts
2967
3000
  import { tool } from "@opencode-ai/plugin";
2968
3001
  async function fetchSessionMessages(client, sessionId) {
@@ -4154,6 +4187,26 @@ var SessionStateRegistry = class {
4154
4187
  startsByCallId: /* @__PURE__ */ new Map(),
4155
4188
  pendingByCallId: /* @__PURE__ */ new Map()
4156
4189
  };
4190
+ // [FIX #312] Model-limit catalog (full rationale in ./model-limits.ts):
4191
+ // lets the messages hook reconcile state.modelContextLimit against the
4192
+ // model named on the request's user message instead of waiting one turn
4193
+ // for the system hook. Shared implementation — the test registry stub
4194
+ // composes the same factory.
4195
+ catalog = createModelLimitCatalog();
4196
+ recordModelLimit(providerId, modelId, limit) {
4197
+ this.catalog.record(providerId, modelId, limit);
4198
+ }
4199
+ resolveModelLimit(providerId, modelId) {
4200
+ return this.catalog.resolve(providerId, modelId);
4201
+ }
4202
+ /**
4203
+ * Best-effort one-time seed from the host's provider catalog
4204
+ * (`client.config.providers()` → GET /config/providers). Never throws;
4205
+ * returns the number of model-limit entries recorded.
4206
+ */
4207
+ hydrateModelLimitsFromClient(client) {
4208
+ return this.catalog.hydrateFromClient(client);
4209
+ }
4157
4210
  get(sessionId) {
4158
4211
  return this.states.get(sessionId);
4159
4212
  }
@@ -4359,11 +4412,7 @@ async function ensureSessionInitialized(client, state, sessionId, logger, messag
4359
4412
  }
4360
4413
  if (typeof persisted.modelContextLimit === "number" && persisted.modelContextLimit > 0) {
4361
4414
  state.modelContextLimit = persisted.modelContextLimit;
4362
- }
4363
- if (typeof persisted.modelProviderID === "string") {
4364
4415
  state.modelProviderID = persisted.modelProviderID;
4365
- }
4366
- if (typeof persisted.modelID === "string") {
4367
4416
  state.modelID = persisted.modelID;
4368
4417
  }
4369
4418
  const applied = applyPendingCompressionDurations(state);
@@ -8973,7 +9022,7 @@ import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
8973
9022
  import { join as join3 } from "path";
8974
9023
  import { existsSync as existsSync3 } from "fs";
8975
9024
  import { homedir as homedir3 } from "os";
8976
- var LOG_VERSION = true ? "1.14.19-pr.313.25" : "dev";
9025
+ var LOG_VERSION = true ? "1.14.19-pr.315.27" : "dev";
8977
9026
  var Logger = class {
8978
9027
  logDir;
8979
9028
  enabled;
@@ -10540,11 +10589,16 @@ function isInternalAgentRequest(messages) {
10540
10589
  }
10541
10590
  function createSystemPromptHandler(registry4, logger, config, prompts) {
10542
10591
  return async (input, output) => {
10592
+ registry4.recordModelLimit(
10593
+ input.model?.providerID,
10594
+ input.model?.id,
10595
+ input.model?.limit?.context
10596
+ );
10543
10597
  const state = input.sessionID ? registry4.get(input.sessionID) : void 0;
10544
10598
  if (state && input.model?.limit?.context) {
10545
10599
  state.modelContextLimit = input.model.limit.context;
10546
- state.modelProviderID = input.model.providerID;
10547
- state.modelID = input.model.id;
10600
+ state.modelProviderID = input.model?.providerID;
10601
+ state.modelID = input.model?.id;
10548
10602
  }
10549
10603
  if (!state || state.isSubAgent && !config.allowSubAgents) {
10550
10604
  return;
@@ -10597,22 +10651,31 @@ function createChatMessageTransformHandler(client, registry4, logger, config, pr
10597
10651
  messages,
10598
10652
  config
10599
10653
  );
10600
- await updatePerTurnState(state, logger, messages);
10601
- const { providerId, modelId } = getModelInfo(messages);
10602
- const prevIdentity = state.modelID;
10603
- const prevModel = prevIdentity ? `${state.modelProviderID}/${prevIdentity}` : void 0;
10604
- if (syncModelIdentity(state, providerId, modelId)) {
10605
- const target = `${providerId}/${modelId}`;
10606
- if (prevIdentity) {
10607
- logger.debug(
10608
- `Model switch detected (${prevModel} \u2192 ${target}); invalidated stale modelContextLimit`
10609
- );
10610
- } else {
10611
- logger.debug(
10612
- `Model identity established (${target}); limit awaits system.transform`
10613
- );
10614
- }
10654
+ const requestModel = lastUserMessage.info.model;
10655
+ const requestModelLimit = registry4.resolveModelLimit(
10656
+ requestModel?.providerID,
10657
+ requestModel?.modelID
10658
+ );
10659
+ if (requestModelLimit !== void 0) {
10660
+ state.modelContextLimit = requestModelLimit;
10661
+ state.modelProviderID = requestModel?.providerID;
10662
+ state.modelID = requestModel?.modelID;
10663
+ } else if (
10664
+ // [FIX #312 fallback] Catalog miss: we cannot CORRECT the
10665
+ // limit, but we can tell when it belongs to a DIFFERENT model.
10666
+ // Invalidate instead of letting every percentage threshold
10667
+ // below run against the wrong window (#312's false positive).
10668
+ // States persisted before this identity pair existed carry no
10669
+ // identity and are treated as stale for the same reason.
10670
+ // Consumers already tolerate undefined — fresh sessions run
10671
+ // with it until the first system.transform sets the pair.
10672
+ requestModel?.providerID && requestModel?.modelID && state.modelContextLimit !== void 0 && (state.modelProviderID !== requestModel.providerID || state.modelID !== requestModel.modelID)
10673
+ ) {
10674
+ state.modelContextLimit = void 0;
10675
+ state.modelProviderID = requestModel.providerID;
10676
+ state.modelID = requestModel.modelID;
10615
10677
  }
10678
+ await updatePerTurnState(state, logger, messages);
10616
10679
  }
10617
10680
  syncCompressPermissionState(state, config, hostPermissions, output.messages);
10618
10681
  if (state.isSubAgent && !config.allowSubAgents) {
@@ -10997,6 +11060,25 @@ var server = (async (ctx) => {
10997
11060
  if (isSecureMode()) {
10998
11061
  configureClientAuth(ctx.client);
10999
11062
  }
11063
+ registry4.hydrateModelLimitsFromClient(ctx.client).then(
11064
+ (recorded) => {
11065
+ if (recorded > 0) {
11066
+ logger.info("Model limit catalog seeded from provider config", {
11067
+ models: recorded
11068
+ });
11069
+ } else {
11070
+ logger.warn(
11071
+ "Model limit catalog seeding recorded no entries \u2014 falling back to per-request refresh (system.transform)"
11072
+ );
11073
+ }
11074
+ },
11075
+ (error) => {
11076
+ logger.warn(
11077
+ "Model limit catalog seeding failed \u2014 falling back to per-request refresh (system.transform)",
11078
+ { error: error instanceof Error ? error.message : String(error) }
11079
+ );
11080
+ }
11081
+ );
11000
11082
  logger.info("DCP initialized");
11001
11083
  startAutoUpdate(ctx, config.autoUpdate);
11002
11084
  const compressToolContext = {