@visorcraft/idlehands 1.1.4 → 1.1.6

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/dist/agent.js CHANGED
@@ -2,6 +2,7 @@ import { OpenAIClient } from './client.js';
2
2
  import { enforceContextBudget, stripThinking, estimateTokensFromMessages, estimateToolSchemaTokens } from './history.js';
3
3
  import * as tools from './tools.js';
4
4
  import { selectHarness } from './harnesses.js';
5
+ import { BASE_MAX_TOKENS, deriveContextWindow, deriveGenerationParams, supportsVisionModel } from './model-customization.js';
5
6
  import { checkExecSafety, checkPathSafety } from './safety.js';
6
7
  import { loadProjectContext } from './context.js';
7
8
  import { loadGitContext, isGitDirty, stashWorkingTree } from './git.js';
@@ -176,6 +177,16 @@ function withCachedExecObservationHint(content) {
176
177
  return `${content}\n${CACHED_EXEC_OBSERVATION_HINT}`;
177
178
  }
178
179
  }
180
+ function readOnlyExecCacheable(content) {
181
+ try {
182
+ const parsed = JSON.parse(content);
183
+ const rc = Number(parsed?.rc ?? NaN);
184
+ return Number.isFinite(rc) && rc === 0;
185
+ }
186
+ catch {
187
+ return false;
188
+ }
189
+ }
179
190
  /** Errors that should break the outer agent loop, not be caught by per-tool handlers */
180
191
  class AgentLoopBreak extends Error {
181
192
  constructor(message) {
@@ -944,26 +955,6 @@ function reviewArtifactStaleReason(artifact, cwd) {
944
955
  }
945
956
  return '';
946
957
  }
947
- function supportsVisionModel(model, modelMeta, harness) {
948
- if (typeof harness.supportsVision === 'boolean')
949
- return harness.supportsVision;
950
- if (typeof modelMeta?.vision === 'boolean')
951
- return modelMeta.vision;
952
- const inputModalities = modelMeta?.input_modalities;
953
- if (Array.isArray(inputModalities) && inputModalities.some((m) => String(m).toLowerCase().includes('image'))) {
954
- return true;
955
- }
956
- const modalities = modelMeta?.modalities;
957
- if (Array.isArray(modalities) && modalities.some((m) => String(m).toLowerCase().includes('image'))) {
958
- return true;
959
- }
960
- const id = model.toLowerCase();
961
- if (/(vision|multimodal|\bvl\b|llava|qwen2\.5-vl|gpt-4o|gemini|claude-3)/i.test(id))
962
- return true;
963
- if (harness.id.includes('vision') || harness.id.includes('vl'))
964
- return true;
965
- return false;
966
- }
967
958
  function normalizeModelsResponse(raw) {
968
959
  if (Array.isArray(raw)) {
969
960
  return {
@@ -1007,8 +998,11 @@ export async function createSession(opts) {
1007
998
  // Try to derive context window from /v1/models (if provided by server).
1008
999
  const explicitContextWindow = cfg.context_window != null;
1009
1000
  const modelMeta = modelsList?.data?.find((m) => m.id === model);
1010
- const derivedCtx = (modelMeta?.context_window ?? modelMeta?.context_length ?? modelMeta?.max_context_length);
1011
- let contextWindow = cfg.context_window ?? derivedCtx ?? 131072;
1001
+ let contextWindow = deriveContextWindow({
1002
+ explicitContextWindow,
1003
+ configuredContextWindow: cfg.context_window,
1004
+ modelMeta,
1005
+ });
1012
1006
  let supportsVision = supportsVisionModel(model, modelMeta, harness);
1013
1007
  if (!cfg.i_know_what_im_doing && contextWindow > 131072) {
1014
1008
  console.warn('[warn] context_window is above 131072; this can increase memory usage and hurt throughput. Use --i-know-what-im-doing to proceed.');
@@ -1018,13 +1012,13 @@ export async function createSession(opts) {
1018
1012
  // whether the harness wants a higher value — harness.defaults.max_tokens wins
1019
1013
  // when it's larger than the base default (16384), unless the user explicitly
1020
1014
  // configured a value in their config file or CLI.
1021
- const BASE_MAX_TOKENS = 16384;
1022
- let maxTokens = cfg.max_tokens ?? BASE_MAX_TOKENS;
1023
- if (maxTokens === BASE_MAX_TOKENS && harness.defaults?.max_tokens && harness.defaults.max_tokens > BASE_MAX_TOKENS) {
1024
- maxTokens = harness.defaults.max_tokens;
1025
- }
1026
- let temperature = cfg.temperature ?? harness.defaults?.temperature ?? 0.2;
1027
- let topP = cfg.top_p ?? harness.defaults?.top_p ?? 0.95;
1015
+ let { maxTokens, temperature, topP } = deriveGenerationParams({
1016
+ harness,
1017
+ configuredMaxTokens: cfg.max_tokens,
1018
+ configuredTemperature: cfg.temperature,
1019
+ configuredTopP: cfg.top_p,
1020
+ baseMaxTokens: BASE_MAX_TOKENS,
1021
+ });
1028
1022
  const harnessVaultMode = harness.defaults?.trifecta?.vaultMode || 'off';
1029
1023
  const vaultMode = (cfg.trifecta?.vault?.mode || harnessVaultMode);
1030
1024
  const vaultEnabled = cfg.trifecta?.enabled !== false && cfg.trifecta?.vault?.enabled !== false;
@@ -1808,18 +1802,19 @@ export async function createSession(opts) {
1808
1802
  harness = selectHarness(model, cfg.harness && cfg.harness.trim() ? cfg.harness.trim() : undefined);
1809
1803
  const nextMeta = modelsList?.data?.find((m) => m.id === model);
1810
1804
  supportsVision = supportsVisionModel(model, nextMeta, harness);
1811
- if (!explicitContextWindow) {
1812
- const derived = asNumber(nextMeta?.context_window, nextMeta?.context_length, nextMeta?.max_context_length);
1813
- if (derived && derived > 0) {
1814
- contextWindow = derived;
1815
- }
1816
- }
1817
- maxTokens = cfg.max_tokens ?? BASE_MAX_TOKENS;
1818
- if (maxTokens === BASE_MAX_TOKENS && harness.defaults?.max_tokens && harness.defaults.max_tokens > BASE_MAX_TOKENS) {
1819
- maxTokens = harness.defaults.max_tokens;
1820
- }
1821
- temperature = cfg.temperature ?? harness.defaults?.temperature ?? 0.2;
1822
- topP = cfg.top_p ?? harness.defaults?.top_p ?? 0.95;
1805
+ contextWindow = deriveContextWindow({
1806
+ explicitContextWindow,
1807
+ configuredContextWindow: cfg.context_window,
1808
+ previousContextWindow: contextWindow,
1809
+ modelMeta: nextMeta,
1810
+ });
1811
+ ({ maxTokens, temperature, topP } = deriveGenerationParams({
1812
+ harness,
1813
+ configuredMaxTokens: cfg.max_tokens,
1814
+ configuredTemperature: cfg.temperature,
1815
+ configuredTopP: cfg.top_p,
1816
+ baseMaxTokens: BASE_MAX_TOKENS,
1817
+ }));
1823
1818
  };
1824
1819
  const setEndpoint = async (endpoint, modelName) => {
1825
1820
  const normalized = endpoint.replace(/\/+$/, '');
@@ -2728,7 +2723,7 @@ export async function createSession(opts) {
2728
2723
  // Successful exec clears blocked-loop counters.
2729
2724
  blockedExecAttemptsBySig.clear();
2730
2725
  const cmd = String(args?.command ?? '');
2731
- if (looksLikeReadOnlyExecCommand(cmd)) {
2726
+ if (looksLikeReadOnlyExecCommand(cmd) && readOnlyExecCacheable(content)) {
2732
2727
  execObservationCacheBySig.set(sig, content);
2733
2728
  }
2734
2729
  // Capture successful test runs for better partial-failure diagnostics.