pikiclaw 0.3.79 → 0.3.80

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.
@@ -25,7 +25,7 @@ export { Q, agentLog, agentWarn, agentError, dedupeStrings, numberOrNull, normal
25
25
  // ── Re-export: session management ───────────────────────────────────────────
26
26
  export { updateSessionMeta, promoteSessionId, recordFork, listPikiclawSessions, findPikiclawSession, getSessionStoredConfig, ensureManagedSession, findManagedThreadSession, stageSessionFiles, mergeManagedAndNativeSessions, getSessions, getSessionTail, getSessionMessages, applyTurnWindow, applyTurnFilter, classifySession, deriveUserStatus, exportSession, importSession, deleteAgentSession, isProcessAlive, isRunningSessionStale, reconcileOrphanedRunningSessions, } from './session.js';
27
27
  // ── Re-export: stream & detection ───────────────────────────────────────────
28
- export { detectAgentBin, listAgents, run, doStream, listModels, resolveAgentModels, getUsage, getAgentBoundModelId, setAgentBoundModelId, } from './stream.js';
28
+ export { detectAgentBin, listAgents, resolveDefaultAgent, run, doStream, listModels, resolveAgentModels, getUsage, getAgentBoundModelId, setAgentBoundModelId, } from './stream.js';
29
29
  // ── Re-export: driver registry ──────────────────────────────────────────────
30
30
  export { registerDriver, getDriver, getDriverCapabilities, allDrivers, allDriverIds, hasDriver, shutdownAllDrivers, } from './driver.js';
31
31
  // ── Re-export: skills ───────────────────────────────────────────────────────
@@ -8,7 +8,7 @@ import path from 'node:path';
8
8
  import { restartManagedBrowser } from '../browser-supervisor.js';
9
9
  import { terminateProcessTree } from '../core/process-control.js';
10
10
  import { AGENT_DETECT_TIMEOUTS, AGENT_STREAM_HARD_KILL_GRACE_MS } from '../core/constants.js';
11
- import { getDriver, allDrivers, getAcceptedProviderKinds } from './driver.js';
11
+ import { getDriver, allDrivers, getAcceptedProviderKinds, hasDriver } from './driver.js';
12
12
  import { resolveAgentInjection, getActiveProfile, getActiveProfileId, getProvider, updateProfile, listProfiles, } from '../model/index.js';
13
13
  import { Q, agentLog, agentWarn, agentError, joinErrorMessages, normalizeErrorMessage, buildStreamPreviewMeta, computeContext, shortValue, isPendingSessionId, dedupeStrings, normalizeStreamPreviewPlan, } from './utils.js';
14
14
  import { saveSessionRecord, setSessionRunState, applySessionRunResult, ensureSessionWorkspace, importFilesIntoWorkspace, syncManagedSessionIdentity, summarizePromptTitle, recordFork, } from './session.js';
@@ -154,6 +154,33 @@ export function detectAgentBin(cmd, agent, options = {}) {
154
154
  export function listAgents(options = {}) {
155
155
  return { agents: allDrivers().map(d => detectAgentBin(d.cmd, d.id, options)) };
156
156
  }
157
+ /**
158
+ * Resolve the *effective* default agent for new conversations.
159
+ *
160
+ * The stored value is only a *preference* — a new conversation can run only an
161
+ * agent whose CLI is actually installed. So when the preference's CLI isn't
162
+ * installed, we clamp to the first installed agent (in driver-registration
163
+ * order: claude → codex → gemini → hermes) instead of surfacing an uninstalled
164
+ * default the user can't run. When the preference *is* installed it always
165
+ * wins, so machines with the historical 'codex' default are unaffected. When
166
+ * nothing is installed we keep the prior behaviour (honour a valid preference,
167
+ * else 'codex') so the result is always defined.
168
+ *
169
+ * Resolution is derived, never persisted: if the user later installs their
170
+ * preferred agent, the original preference is honoured again automatically.
171
+ * `agents` is injected (defaults to live detection) so the resolution is a pure
172
+ * function of (preference, install-state) and trivially testable.
173
+ */
174
+ export function resolveDefaultAgent(preferred, agents = listAgents().agents) {
175
+ const want = typeof preferred === 'string' ? preferred.trim().toLowerCase() : '';
176
+ const wantValid = !!want && hasDriver(want);
177
+ const installed = agents.filter(a => a.installed).map(a => a.agent);
178
+ if (wantValid && installed.includes(want))
179
+ return want;
180
+ if (installed.length)
181
+ return installed[0];
182
+ return wantValid ? want : 'codex';
183
+ }
157
184
  // ---------------------------------------------------------------------------
158
185
  // Shared CLI spawn framework (used by driver-claude.ts, driver-gemini.ts)
159
186
  // ---------------------------------------------------------------------------
package/dist/bot/bot.js CHANGED
@@ -7,7 +7,7 @@ import os from 'node:os';
7
7
  import path from 'node:path';
8
8
  import { execSync, spawn } from 'node:child_process';
9
9
  import { getActiveUserConfig, loadWorkspaces, onUserConfigChange, resolveUserWorkdir, setUserWorkdir, updateUserConfig } from '../core/config/user-config.js';
10
- import { doStream, ensureManagedSession, findManagedThreadSession, getSessionStoredConfig, getUsage, initializeProjectSkills, listAgents, resolveAgentModels, listSkills, stageSessionFiles, reconcileOrphanedRunningSessions, getAgentBoundModelId, setAgentBoundModelId, collapseSkillPrompt, readGoal, accountTurn, shouldContinueAfterTurn, renderContinuationPrompt, renderBudgetLimitPrompt, bumpContinuationCount, pauseGoal, resumeGoal, setGoal as setGoalState, clearGoal as clearGoalState, setCodexGoal, getCodexGoal, clearCodexGoal, pauseCodexGoal, resumeCodexGoal, getClaudeNativeGoal, buildClaudeSetGoalPrompt, buildClaudeClearGoalPrompt, isPendingSessionId, } from '../agent/index.js';
10
+ import { doStream, ensureManagedSession, findManagedThreadSession, getSessionStoredConfig, getUsage, initializeProjectSkills, listAgents, resolveAgentModels, resolveDefaultAgent, listSkills, stageSessionFiles, reconcileOrphanedRunningSessions, getAgentBoundModelId, setAgentBoundModelId, collapseSkillPrompt, readGoal, accountTurn, shouldContinueAfterTurn, renderContinuationPrompt, renderBudgetLimitPrompt, bumpContinuationCount, pauseGoal, resumeGoal, setGoal as setGoalState, clearGoal as clearGoalState, setCodexGoal, getCodexGoal, clearCodexGoal, pauseCodexGoal, resumeCodexGoal, getClaudeNativeGoal, buildClaudeSetGoalPrompt, buildClaudeClearGoalPrompt, isPendingSessionId, } from '../agent/index.js';
11
11
  import { compactForHandover, describeHandoverRef } from '../agent/handover.js';
12
12
  import { getActiveProfileId, setActiveProfile } from '../model/index.js';
13
13
  import { querySessions, querySessionTail, updateSession, } from './session-hub.js';
@@ -2229,7 +2229,11 @@ export class Bot {
2229
2229
  else if (nextWorkdir !== this.workdir) {
2230
2230
  this.switchWorkdir(nextWorkdir, { persist: false });
2231
2231
  }
2232
- const nextDefaultAgent = normalizeAgent(String(config.defaultAgent || 'codex').trim().toLowerCase() || 'codex');
2232
+ // The configured value is a *preference* (baseline 'codex' when unset);
2233
+ // clamp it to an installed agent so a fresh machine whose preferred CLI
2234
+ // isn't installed still routes new conversations to one that can actually
2235
+ // run, instead of surfacing an uninstalled default.
2236
+ const nextDefaultAgent = resolveDefaultAgent(config.defaultAgent || 'codex', listAgents().agents);
2233
2237
  if (opts.initial)
2234
2238
  this.defaultAgent = nextDefaultAgent;
2235
2239
  else if (nextDefaultAgent !== this.defaultAgent)
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { EventEmitter } from 'node:events';
9
9
  import { applyChannelEnvFallback, loadUserConfig, resolveUserWorkdir } from '../core/config/user-config.js';
10
- import { listAgents } from '../agent/index.js';
10
+ import { listAgents, resolveDefaultAgent } from '../agent/index.js';
11
11
  import { collectSetupState } from '../cli/onboarding.js';
12
12
  import { validateDingtalkConfig, validateDiscordConfig, validateFeishuConfig, validateSlackConfig, validateTelegramConfig, validateWecomConfig, validateWeixinConfig, } from '../core/config/validation.js';
13
13
  import { shouldCacheChannelStates } from '../channels/states.js';
@@ -205,8 +205,11 @@ class Runtime {
205
205
  getRuntimeDefaultAgent(config) {
206
206
  if (this.botRef)
207
207
  return this.botRef.defaultAgent;
208
- const raw = String(this.runtimePrefs.defaultAgent || config.defaultAgent || 'codex').trim().toLowerCase();
209
- return this.isAgent(raw) ? raw : 'codex';
208
+ // No bot yet (e.g. setup flow): resolve the stored preference (baseline
209
+ // 'codex' when unset) against installed CLIs so the dashboard never
210
+ // surfaces an uninstalled default the user can't run.
211
+ const preferred = this.runtimePrefs.defaultAgent || config.defaultAgent || 'codex';
212
+ return resolveDefaultAgent(preferred, listAgents().agents);
210
213
  }
211
214
  setModelEnv(agent, value) {
212
215
  setAgentModelEnv(agent, value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pikiclaw",
3
- "version": "0.3.79",
3
+ "version": "0.3.80",
4
4
  "description": "Put the world's smartest AI agents in your pocket. Command local Claude & Gemini via IM. | 让最好用的 IM 变成你电脑上的顶级 Agent 控制台",
5
5
  "type": "module",
6
6
  "bin": {