@pasko70/pibo 1.7.8 → 1.7.10

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.
@@ -40,7 +40,10 @@ export function syncChatUserSkills(options) {
40
40
  enabledSkillByName.set(skill.name, skill);
41
41
  }
42
42
  const enabledNames = new Set(enabledSkillByName.keys());
43
- const syncedNames = previouslySyncedNames ?? new Set();
43
+ const syncedNames = new Set([
44
+ ...catalogSkills.filter((skill) => skill.kind === "user").map((skill) => skill.name),
45
+ ...(previouslySyncedNames ?? []),
46
+ ]);
44
47
  // Unregister disabled, removed, or now built-in user skills. Avoid removing a
45
48
  // built-in/plugin skill if a promoted user skill has the same name.
46
49
  for (const name of syncedNames) {
@@ -59,16 +62,15 @@ export function syncChatUserSkills(options) {
59
62
  unregisterSkill(skill.name);
60
63
  registerSkill({ name: skill.name, path: skill.path, enabled: true, kind: "user" });
61
64
  }
62
- else if (!syncedNames.has(skill.name)) {
65
+ else if (!catalogSkill) {
63
66
  registerSkill({ name: skill.name, path: skill.path, enabled: true, kind: "user" });
64
67
  }
65
68
  }
66
69
  setSyncedUserSkillNames(enabledNames);
67
70
  }
68
71
  function assertUserSkillNameIsAvailable(options) {
69
- const { userSkillManager, channelContext, name, currentSkillId } = options;
70
- const currentSkill = currentSkillId ? userSkillManager.get(currentSkillId) : undefined;
71
- const conflict = (channelContext.getCapabilityCatalog?.().skills ?? []).find((skill) => (skill.name === name && skill.kind !== "user" && (!currentSkill || currentSkill.name !== name)));
72
+ const { channelContext, name } = options;
73
+ const conflict = (channelContext.getCapabilityCatalog?.().skills ?? []).find((skill) => (skill.name === name && skill.kind !== "user"));
72
74
  if (conflict) {
73
75
  throw new PiboWebHttpError(`Skill name "${name}" conflicts with an existing registered skill`, 409);
74
76
  }
@@ -105,7 +107,7 @@ export async function handleChatUserSkillRoute(options) {
105
107
  const body = await readJsonBody(request);
106
108
  const scope = bodyScope(body);
107
109
  const name = normalizeUserSkillName(body.name);
108
- assertUserSkillNameIsAvailable({ userSkillManager, channelContext, name });
110
+ assertUserSkillNameIsAvailable({ channelContext, name });
109
111
  const skill = userSkillManager.create({
110
112
  name,
111
113
  description: normalizeUserSkillDescription(body.description ?? ""),
@@ -119,7 +121,7 @@ export async function handleChatUserSkillRoute(options) {
119
121
  const scope = bodyScope(body);
120
122
  const skill = await userSkillManager.installFromUrl(normalizeUserSkillUrl(body.url), scope);
121
123
  try {
122
- assertUserSkillNameIsAvailable({ userSkillManager, channelContext, name: skill.name, currentSkillId: skill.id });
124
+ assertUserSkillNameIsAvailable({ channelContext, name: skill.name });
123
125
  }
124
126
  catch (error) {
125
127
  userSkillManager.remove(skill.id, scope);
@@ -157,12 +159,7 @@ export async function handleChatUserSkillRoute(options) {
157
159
  const nextName = input.name ?? existing.name;
158
160
  const nextEnabled = input.enabled ?? existing.enabled;
159
161
  if (nextEnabled) {
160
- assertUserSkillNameIsAvailable({
161
- userSkillManager,
162
- channelContext,
163
- name: nextName,
164
- currentSkillId: existing.id,
165
- });
162
+ assertUserSkillNameIsAvailable({ channelContext, name: nextName });
166
163
  }
167
164
  const skill = userSkillManager.update(existing.id, input, scope);
168
165
  syncAndInvalidate(options);
@@ -1,4 +1,4 @@
1
- import { createAgentSessionServices } from "@mariozechner/pi-coding-agent";
1
+ import { createAgentSessionServices } from "@earendil-works/pi-coding-agent";
2
2
  import { registerMiniMaxProvider } from "../../providers/minimax.js";
3
3
  import { registerGlmProvider } from "../../providers/glm.js";
4
4
  import { registerOpenAiGpt56Models } from "../../providers/openai-gpt56.js";
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, statSync } from "node:fs";
3
3
  import { homedir } from "node:os";
4
4
  import { join } from "node:path";
5
- import { parseSessionEntries, SessionManager } from "@mariozechner/pi-coding-agent";
5
+ import { parseSessionEntries, SessionManager } from "@earendil-works/pi-coding-agent";
6
6
  import { buildTraceViewFromEvents } from "../../shared/trace-engine.js";
7
7
  import { isChatWebSessionArchived } from "./session-metadata.js";
8
8
  import { workflowSessionKindFromMetadata } from "../../sessions/workflow-session-kind.js";
@@ -3029,7 +3029,10 @@ export function createChatWebApp(options = {}) {
3029
3029
  persistenceMetrics: createPersistenceMetrics(),
3030
3030
  resourceMetrics: createResourceMetrics(),
3031
3031
  eventLoopDelay,
3032
- userSkillManager: new ScopedUserSkillManager({ globalRoot: os.homedir(), workspaceRoot: process.cwd() }),
3032
+ userSkillManager: new ScopedUserSkillManager({
3033
+ globalRoot: options.userSkillGlobalRoot ?? os.homedir(),
3034
+ workspaceRoot: options.userSkillWorkspaceRoot ?? process.cwd(),
3035
+ }),
3033
3036
  workflowDraftStore: new ChatWorkflowDraftStore(dataStore),
3034
3037
  workflowPublishedVersionStore: new ChatWorkflowPublishedVersionStore(dataStore),
3035
3038
  workflowArchiveStore: new ChatWorkflowArchiveStore(dataStore),
@@ -1,4 +1,4 @@
1
- import { AuthStorage } from "@mariozechner/pi-coding-agent";
1
+ import { AuthStorage } from "@earendil-works/pi-coding-agent";
2
2
  import { randomBytes } from "node:crypto";
3
3
  const OPENAI_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
4
4
  const OPENAI_AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize";
@@ -1,4 +1,4 @@
1
- import { AuthStorage } from "@mariozechner/pi-coding-agent";
1
+ import { AuthStorage } from "@earendil-works/pi-coding-agent";
2
2
  const OPENAI_CODEX_PROVIDER = "openai-codex";
3
3
  const OPENAI_CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
4
4
  const OPENAI_JWT_CLAIM_PATH = "https://api.openai.com/auth";
@@ -2,8 +2,8 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { readFile, writeFile } from "node:fs/promises";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { completeSimple } from "@mariozechner/pi-ai";
6
- import { buildSessionContext, convertToLlm, serializeConversation, } from "@mariozechner/pi-coding-agent";
5
+ import { completeSimple } from "@earendil-works/pi-ai/compat";
6
+ import { buildSessionContext, convertToLlm, serializeConversation, } from "@earendil-works/pi-coding-agent";
7
7
  const PROJECT_ROOT = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
8
8
  export const PIBO_LIBRARY_COMPACTION_PROMPT_PATH = resolve(PROJECT_ROOT, "context/pibo-compaction-prompt.md");
9
9
  function getCompactionPromptStatePath(cwd) {
@@ -244,7 +244,7 @@ async function readSkillMarkdown(path) {
244
244
  }
245
245
  }
246
246
  // Skills are advertised to the model via a small XML summary in the system
247
- // prompt (see formatSkillsForPrompt in @mariozechner/pi-coding-agent). Only the
247
+ // prompt (see formatSkillsForPrompt in @earendil-works/pi-coding-agent). Only the
248
248
  // name, description, and location land in the prompt; the full SKILL.md body
249
249
  // is loaded lazily by the model via the read tool. Mirror that exact
250
250
  // per-skill entry shape here so the inspector's token estimate matches what
@@ -1,4 +1,4 @@
1
- import { DEFAULT_COMPACTION_SETTINGS, buildSessionContext, estimateTokens, } from "@mariozechner/pi-coding-agent";
1
+ import { DEFAULT_COMPACTION_SETTINGS, buildSessionContext, estimateTokens, } from "@earendil-works/pi-coding-agent";
2
2
  export const PIBO_CONTEXT_GUARD_NOTICE = "Context safety interrupted this response before adding it to long-term context. Pibo is compacting the session before continuing.";
3
3
  const DEFAULT_MIN_COMPACTION_RESERVE_TOKENS = 1024;
4
4
  const FALLBACK_CONTEXT_WINDOW = 0;
@@ -1,4 +1,4 @@
1
- import { SessionManager, shouldCompact } from "@mariozechner/pi-coding-agent";
1
+ import { SessionManager, shouldCompact } from "@earendil-works/pi-coding-agent";
2
2
  import { getOpenAiCodexProviderUsageForActiveModel } from "../auth/openai-codex-usage.js";
3
3
  import { normalizeSessionErrorDetails, runtimeSessionErrorDetails } from "./session-errors.js";
4
4
  import { expandInlineSkills } from "./skill-expansion.js";
@@ -1,6 +1,6 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { isAbsolute, resolve } from "node:path";
3
- import { AuthStorage, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createBashToolDefinition, getAgentDir, InteractiveMode, SessionManager, } from "@mariozechner/pi-coding-agent";
3
+ import { AuthStorage, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createBashToolDefinition, getAgentDir, InteractiveMode, SessionManager, } from "@earendil-works/pi-coding-agent";
4
4
  import { DEFAULT_BUILTIN_TOOL_NAMES, InitialSessionContext, } from "./profiles.js";
5
5
  import { loadPiboModelDefaults, selectRequestedModelProfile, selectRequestedThinkingLevel } from "./model-defaults.js";
6
6
  import { createDefaultPiboProfile } from "../plugins/builtin.js";
@@ -1,5 +1,5 @@
1
- import { Type } from "@mariozechner/pi-ai";
2
- import { defineTool } from "@mariozechner/pi-coding-agent";
1
+ import { Type } from "@earendil-works/pi-ai";
2
+ import { defineTool } from "@earendil-works/pi-coding-agent";
3
3
  import { sendGatewayMessageAndWaitForReply } from "./request.js";
4
4
  export function createPiboGatewaySendTool(sendGatewayMessage = sendGatewayMessageAndWaitForReply) {
5
5
  return defineTool({
@@ -1,6 +1,7 @@
1
1
  import { createDefaultPiboPlugins } from "../plugins/builtin.js";
2
2
  import { createPiboBetterAuthPlugin } from "../plugins/better-auth.js";
3
3
  import { createPiboChatCustomAgentProfilesPlugin } from "../plugins/chat-custom-agents.js";
4
+ import { createPiboChatUserSkillsPlugin } from "../plugins/chat-user-skills.js";
4
5
  import { createPiboChatWebPlugin } from "../plugins/chat-web.js";
5
6
  import { createPiboChatVscodeWebPlugin } from "../plugins/chat-vscode-web.js";
6
7
  import { createPiboContextFilesPlugin } from "../plugins/context-files.js";
@@ -145,6 +146,10 @@ export function createWebPiboPluginRegistry(options = {}) {
145
146
  useDevAuth ? createPiboDevAuthPlugin() : createPiboBetterAuthPlugin(resolvedOptions.auth),
146
147
  createPiboWebHostPlugin({ announce: false, canonicalBaseURL: useDevAuth ? undefined : authBaseURL(resolvedOptions), gatewayMode: webGatewayMode(resolvedOptions, useDevAuth), ...resolvedOptions.web }),
147
148
  createPiboCronPlugin({ cronStorePath: resolvedOptions.chat?.cronStorePath, dataStorePath: resolvedOptions.chat?.dataStorePath, dataPayloadRootDir: resolvedOptions.chat?.dataPayloadRootDir }),
149
+ createPiboChatUserSkillsPlugin({
150
+ globalRoot: resolvedOptions.chat?.userSkillGlobalRoot,
151
+ workspaceRoot: resolvedOptions.chat?.userSkillWorkspaceRoot,
152
+ }),
148
153
  createPiboChatCustomAgentProfilesPlugin({ agentStorePath: resolvedOptions.chat?.agentStorePath }),
149
154
  createPiboRalphPlugin({ ralphStorePath: resolvedOptions.chat?.ralphStorePath, dataStorePath: resolvedOptions.chat?.dataStorePath, dataPayloadRootDir: resolvedOptions.chat?.dataPayloadRootDir }),
150
155
  createPiboContextFilesPlugin(resolvedOptions.contextFiles),
@@ -1,5 +1,5 @@
1
- import { AssistantMessageComponent, ToolExecutionComponent, UserMessageComponent, } from "@mariozechner/pi-coding-agent";
2
- import { Container, Spacer } from "@mariozechner/pi-tui";
1
+ import { AssistantMessageComponent, ToolExecutionComponent, UserMessageComponent, } from "@earendil-works/pi-coding-agent";
2
+ import { Container, Spacer } from "@earendil-works/pi-tui";
3
3
  import { parsePiboThinkingLevel } from "../core/thinking.js";
4
4
  const LOCAL_MESSAGE_TYPE = "pibo.local-routed";
5
5
  const STREAMING_WIDGET_KEY = "pibo.local.streaming";
@@ -2,7 +2,7 @@ import { execFile } from "node:child_process";
2
2
  import { existsSync, readFileSync, statSync } from "node:fs";
3
3
  import { basename, extname, resolve } from "node:path";
4
4
  import { promisify } from "node:util";
5
- import { DefaultPackageManager, getAgentDir, SettingsManager, } from "@mariozechner/pi-coding-agent";
5
+ import { DefaultPackageManager, getAgentDir, SettingsManager, } from "@earendil-works/pi-coding-agent";
6
6
  import { installOrResolvePiPackage } from "./installer.js";
7
7
  const execFileAsync = promisify(execFile);
8
8
  export async function inspectPiPackageSource(source, cwd = process.cwd()) {
@@ -0,0 +1,41 @@
1
+ import os from "node:os";
2
+ import { ScopedUserSkillManager } from "../user-skills/manager.js";
3
+ import { definePiboPlugin } from "./registry.js";
4
+ export function createPiboChatUserSkillsPlugin(options = {}) {
5
+ return definePiboPlugin({
6
+ id: "pibo.chat-user-skills",
7
+ name: "Pibo Chat User Skills",
8
+ register(api) {
9
+ const manager = new ScopedUserSkillManager({
10
+ globalRoot: options.globalRoot ?? os.homedir(),
11
+ workspaceRoot: options.workspaceRoot ?? process.cwd(),
12
+ });
13
+ const userSkills = [];
14
+ for (const [scope, scopedManager] of [["global", manager.global], ["workspace", manager.workspace]]) {
15
+ try {
16
+ userSkills.push(...scopedManager.list());
17
+ }
18
+ catch (error) {
19
+ console.warn(`[pibo] Skipping ${scope} startup user-skill registration: ${error instanceof Error ? error.message : String(error)}`);
20
+ }
21
+ }
22
+ const enabledSkillByName = new Map();
23
+ for (const skill of userSkills) {
24
+ if (!skill.enabled)
25
+ continue;
26
+ const existing = enabledSkillByName.get(skill.name);
27
+ if (!existing || skill.scope === "workspace")
28
+ enabledSkillByName.set(skill.name, skill);
29
+ }
30
+ for (const skill of enabledSkillByName.values()) {
31
+ try {
32
+ api.registerSkill({ name: skill.name, path: skill.path, enabled: true, kind: "user" });
33
+ }
34
+ catch (error) {
35
+ if (!(error instanceof Error) || error.message !== `Duplicate skill "${skill.name}"`)
36
+ throw error;
37
+ }
38
+ }
39
+ },
40
+ });
41
+ }
@@ -1,4 +1,4 @@
1
- import { getModels } from "@mariozechner/pi-ai";
1
+ import { getModels } from "@earendil-works/pi-ai/compat";
2
2
  import { OPENAI_COMPLETIONS_API, registerOpenAiCompatProvider, resetOpenAiCompatProviderRegistration, unregisterOpenAiCompatProvider, } from "./openai-compat.js";
3
3
  export const GLM_PROVIDER_ID = "glm";
4
4
  export const GLM_DEFAULT_BASE_URL = "https://api.z.ai/api/coding/paas/v4";
@@ -1,4 +1,4 @@
1
- import { getModels } from "@mariozechner/pi-ai";
1
+ import { getModels } from "@earendil-works/pi-ai/compat";
2
2
  import { OPENAI_COMPLETIONS_API, registerOpenAiCompatProvider, resetOpenAiCompatProviderRegistration, unregisterOpenAiCompatProvider, } from "./openai-compat.js";
3
3
  export const MINIMAX_PROVIDER_ID = "minimax";
4
4
  export const MINIMAX_CN_PROVIDER_ID = "minimax-cn";
@@ -1,13 +1,15 @@
1
- import { getModels } from "@mariozechner/pi-ai";
1
+ import { getModels } from "@earendil-works/pi-ai/compat";
2
+ import { getOAuthProvider } from "@earendil-works/pi-ai/oauth";
2
3
  export const OPENAI_PROVIDER_ID = "openai";
3
4
  export const OPENAI_RESPONSES_API = "openai-responses";
4
5
  export const OPENAI_BASE_URL = "https://api.openai.com/v1";
5
6
  export const OPENAI_API_KEY_ENV = "OPENAI_API_KEY";
6
- const GPT_56_CONTEXT_WINDOW = 1_050_000;
7
+ export const OPENAI_CODEX_PROVIDER_ID = "openai-codex";
8
+ export const OPENAI_CODEX_RESPONSES_API = "openai-codex-responses";
9
+ export const OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
10
+ const OPENAI_GPT_56_CONTEXT_WINDOW = 1_050_000;
11
+ const OPENAI_CODEX_GPT_56_CONTEXT_WINDOW = 272_000;
7
12
  const GPT_56_MAX_TOKENS = 128_000;
8
- // Register GPT-5.6 on the normal OpenAI API provider only.
9
- // This keeps Pibo on the existing token-plan path (OPENAI_API_KEY) and does not
10
- // route these models through ChatGPT/Codex OAuth (`openai-codex`).
11
13
  export const OPENAI_GPT_56_MODELS = [
12
14
  {
13
15
  id: "gpt-5.6",
@@ -30,62 +32,122 @@ export const OPENAI_GPT_56_MODELS = [
30
32
  cost: { input: 1, output: 6, cacheRead: 0.1, cacheWrite: 1.25 },
31
33
  },
32
34
  ];
35
+ const OPENAI_GPT_56_MODEL_IDS = new Set(OPENAI_GPT_56_MODELS.map((model) => model.id));
33
36
  export function getBuiltInOpenAiModels() {
34
- try {
35
- return getModels(OPENAI_PROVIDER_ID);
36
- }
37
- catch {
38
- return [];
39
- }
37
+ return getBuiltInProviderModels(OPENAI_PROVIDER_ID);
38
+ }
39
+ export function getBuiltInOpenAiCodexModels() {
40
+ return getBuiltInProviderModels(OPENAI_CODEX_PROVIDER_ID);
40
41
  }
41
42
  export function buildOpenAiGpt56Models(baseModels = getBuiltInOpenAiModels()) {
42
- const openAiBaseModels = baseModels
43
- .filter((model) => model.provider === OPENAI_PROVIDER_ID)
44
- .map((model) => ({
45
- ...model,
46
- input: [...model.input],
47
- cost: { ...model.cost },
48
- headers: model.headers ? { ...model.headers } : undefined,
49
- compat: model.compat ? { ...model.compat } : undefined,
50
- }));
51
- const existingIds = new Set(openAiBaseModels.map((model) => model.id));
52
- const additions = OPENAI_GPT_56_MODELS
53
- .filter((model) => !existingIds.has(model.id))
54
- .map(openAiGpt56ModelToRegistryModel);
55
- return [...openAiBaseModels, ...additions];
43
+ return buildProviderGpt56Models({
44
+ providerId: OPENAI_PROVIDER_ID,
45
+ api: OPENAI_RESPONSES_API,
46
+ baseUrl: OPENAI_BASE_URL,
47
+ contextWindow: OPENAI_GPT_56_CONTEXT_WINDOW,
48
+ thinkingLevelMap: { off: null, xhigh: "xhigh" },
49
+ baseModels,
50
+ modelCost: (model) => model.cost,
51
+ });
52
+ }
53
+ export function buildOpenAiCodexGpt56Models(baseModels = getBuiltInOpenAiCodexModels()) {
54
+ return buildProviderGpt56Models({
55
+ providerId: OPENAI_CODEX_PROVIDER_ID,
56
+ api: OPENAI_CODEX_RESPONSES_API,
57
+ baseUrl: OPENAI_CODEX_BASE_URL,
58
+ contextWindow: OPENAI_CODEX_GPT_56_CONTEXT_WINDOW,
59
+ thinkingLevelMap: { xhigh: "xhigh", minimal: "low" },
60
+ baseModels,
61
+ modelCost: (model) => ({ ...model.cost, cacheWrite: 0 }),
62
+ });
56
63
  }
57
64
  export function registerOpenAiGpt56Models(modelRegistry, options = {}) {
58
- const baseModels = options.baseModels ?? getBuiltInOpenAiModels();
59
- const models = buildOpenAiGpt56Models(baseModels);
60
- const baseOpenAiIds = new Set(baseModels.filter((model) => model.provider === OPENAI_PROVIDER_ID).map((model) => model.id));
61
- const added = OPENAI_GPT_56_MODELS.filter((model) => !baseOpenAiIds.has(model.id)).length;
65
+ const baseOpenAiModels = options.baseOpenAiModels ?? getBuiltInOpenAiModels();
66
+ const openAiModels = buildOpenAiGpt56Models(baseOpenAiModels);
67
+ const openAiAdded = countMissingGpt56Models(baseOpenAiModels, OPENAI_PROVIDER_ID);
62
68
  modelRegistry.registerProvider(OPENAI_PROVIDER_ID, {
63
69
  baseUrl: OPENAI_BASE_URL,
64
70
  api: OPENAI_RESPONSES_API,
65
71
  apiKey: OPENAI_API_KEY_ENV,
66
- models,
72
+ models: openAiModels,
73
+ });
74
+ const baseOpenAiCodexModels = options.baseOpenAiCodexModels ?? getBuiltInOpenAiCodexModels();
75
+ const openAiCodexModels = buildOpenAiCodexGpt56Models(baseOpenAiCodexModels);
76
+ const openAiCodexAdded = countMissingGpt56Models(baseOpenAiCodexModels, OPENAI_CODEX_PROVIDER_ID);
77
+ const openAiCodexOAuth = getOpenAiCodexOAuthConfig();
78
+ modelRegistry.registerProvider(OPENAI_CODEX_PROVIDER_ID, {
79
+ name: openAiCodexOAuth.name,
80
+ baseUrl: OPENAI_CODEX_BASE_URL,
81
+ api: OPENAI_CODEX_RESPONSES_API,
82
+ oauth: openAiCodexOAuth,
83
+ models: openAiCodexModels,
67
84
  });
68
- return { registered: true, models: models.length, added };
85
+ return {
86
+ registered: true,
87
+ providers: 2,
88
+ models: openAiModels.length + openAiCodexModels.length,
89
+ added: openAiAdded + openAiCodexAdded,
90
+ };
69
91
  }
70
92
  export function findOpenAiGpt56Model(modelRegistry, model) {
71
- if (model?.provider !== OPENAI_PROVIDER_ID || !model.id)
93
+ if (!model?.provider || !model.id)
94
+ return undefined;
95
+ if (model.provider !== OPENAI_PROVIDER_ID && model.provider !== OPENAI_CODEX_PROVIDER_ID)
72
96
  return undefined;
73
- if (!OPENAI_GPT_56_MODELS.some((candidate) => candidate.id === model.id))
97
+ if (!OPENAI_GPT_56_MODEL_IDS.has(model.id))
74
98
  return undefined;
75
- return modelRegistry.find(OPENAI_PROVIDER_ID, model.id);
99
+ return modelRegistry.find(model.provider, model.id);
76
100
  }
77
- function openAiGpt56ModelToRegistryModel(model) {
101
+ function getBuiltInProviderModels(providerId) {
102
+ try {
103
+ return getModels(providerId);
104
+ }
105
+ catch {
106
+ return [];
107
+ }
108
+ }
109
+ function buildProviderGpt56Models(options) {
110
+ const providerBaseModels = options.baseModels
111
+ .filter((model) => model.provider === options.providerId)
112
+ .map(cloneModel);
113
+ const existingIds = new Set(providerBaseModels.map((model) => model.id));
114
+ const additions = OPENAI_GPT_56_MODELS
115
+ .filter((model) => !existingIds.has(model.id))
116
+ .map((model) => openAiGpt56ModelToRegistryModel(model, options));
117
+ return [...providerBaseModels, ...additions];
118
+ }
119
+ function countMissingGpt56Models(baseModels, providerId) {
120
+ const existingIds = new Set(baseModels.filter((model) => model.provider === providerId).map((model) => model.id));
121
+ return OPENAI_GPT_56_MODELS.filter((model) => !existingIds.has(model.id)).length;
122
+ }
123
+ function cloneModel(model) {
124
+ return {
125
+ ...model,
126
+ input: [...model.input],
127
+ cost: { ...model.cost },
128
+ headers: model.headers ? { ...model.headers } : undefined,
129
+ compat: model.compat ? { ...model.compat } : undefined,
130
+ };
131
+ }
132
+ function openAiGpt56ModelToRegistryModel(model, options) {
78
133
  return {
79
134
  id: model.id,
80
135
  name: model.name,
81
- api: OPENAI_RESPONSES_API,
82
- provider: OPENAI_PROVIDER_ID,
83
- baseUrl: OPENAI_BASE_URL,
136
+ api: options.api,
137
+ provider: options.providerId,
138
+ baseUrl: options.baseUrl,
84
139
  reasoning: true,
85
- thinkingLevelMap: { off: null, xhigh: "xhigh" },
140
+ thinkingLevelMap: options.thinkingLevelMap,
86
141
  input: ["text", "image"],
87
- cost: { ...model.cost },
88
- contextWindow: GPT_56_CONTEXT_WINDOW,
142
+ cost: options.modelCost(model),
143
+ contextWindow: options.contextWindow,
89
144
  maxTokens: GPT_56_MAX_TOKENS,
90
145
  };
91
146
  }
147
+ function getOpenAiCodexOAuthConfig() {
148
+ const provider = getOAuthProvider(OPENAI_CODEX_PROVIDER_ID);
149
+ if (!provider)
150
+ throw new Error("OpenAI Codex OAuth provider is unavailable.");
151
+ const { id: _id, ...oauth } = provider;
152
+ return oauth;
153
+ }
@@ -48,7 +48,7 @@ export class PiboRalphService {
48
48
  this.roomService = new ChatRoomService(this.dataStore);
49
49
  this.intervalMs = options.intervalMs ?? 5_000;
50
50
  this.maxConcurrentRuns = Math.max(1, options.maxConcurrentRuns ?? 2);
51
- this.runTimeoutMs = options.runTimeoutMs ?? 30 * 60_000;
51
+ this.runTimeoutMs = options.runTimeoutMs;
52
52
  }
53
53
  start() { if (!this.stopped)
54
54
  return; this.stopped = false; this.store.recoverInterruptedRuns(); this.unsubscribeProductEvents = this.options.context.subscribeProductEvents?.((event) => this.handleProductEvent(event)); this.arm(250); }
@@ -256,18 +256,23 @@ export class PiboRalphService {
256
256
  } const room = this.roomService.ensureDefaultRoom({ name: 'Shared Chat' }); return { roomId: room.id, workspace: room.workspace ?? getDefaultPiboWorkspace() }; }
257
257
  async emitMessageAndWait(piboSessionId, text, options = {}) {
258
258
  const eventId = `ralph_msg_${randomUUID()}`;
259
- return await new Promise((resolve, reject) => { let settled = false; let deltaAnswer = ''; let finalAnswer = ''; let lastSessionError; let unsubscribe; const timeout = setTimeout(() => finish(new Error(lastSessionError ? `Ralph run timed out after session error: ${lastSessionError}` : 'Ralph run timed out')), this.runTimeoutMs); const finish = (error) => { if (settled)
260
- return; settled = true; clearTimeout(timeout); unsubscribe?.(); if (error)
259
+ return await new Promise((resolve, reject) => { let settled = false; let deltaAnswer = ''; let finalAnswer = ''; let lastSessionError; let unsubscribe; let timeout; const finish = (error) => { if (settled)
260
+ return; settled = true; if (timeout)
261
+ clearTimeout(timeout); unsubscribe?.(); if (error)
261
262
  reject(error);
262
263
  else
263
- resolve(finalAnswer || deltaAnswer); }; unsubscribe = this.options.context.subscribe((event) => { if (event.piboSessionId !== piboSessionId)
264
+ resolve(finalAnswer || deltaAnswer); }; if (this.runTimeoutMs !== undefined)
265
+ timeout = setTimeout(() => finish(new Error(lastSessionError ? `Ralph run timed out after session error: ${lastSessionError}` : 'Ralph run timed out')), this.runTimeoutMs); unsubscribe = this.options.context.subscribe((event) => { if (event.piboSessionId !== piboSessionId)
264
266
  return; if ('eventId' in event && event.eventId !== eventId)
265
267
  return; if (event.type === 'assistant_delta')
266
- deltaAnswer += event.text; if (event.type === 'assistant_message')
267
- finalAnswer = event.text; if (event.type === 'message_finished')
268
- finish(); if (event.type === 'session_error') {
268
+ deltaAnswer += event.text; if (event.type === 'assistant_message') {
269
+ finalAnswer = event.text;
270
+ lastSessionError = undefined;
271
+ } if (event.type === 'message_finished')
272
+ finish(lastSessionError ? new Error(lastSessionError) : undefined); if (event.type === 'session_error') {
269
273
  lastSessionError = event.error;
270
- if (options.isCancelled?.())
274
+ const providerAttempt = event.errorDetails?.origin === 'provider' && Boolean(event.errorDetails.api || event.errorDetails.provider || event.errorDetails.model);
275
+ if (!providerAttempt || options.isCancelled?.())
271
276
  finish(new Error(event.error));
272
277
  } }); this.options.context.emit({ type: 'message', piboSessionId, id: eventId, source: 'service', text }).catch((error) => finish(error instanceof Error ? error : new Error(String(error)))); });
273
278
  }
@@ -1,5 +1,5 @@
1
- import { StringEnum, Type } from "@mariozechner/pi-ai";
2
- import { defineTool } from "@mariozechner/pi-coding-agent";
1
+ import { StringEnum, Type } from "@earendil-works/pi-ai";
2
+ import { defineTool } from "@earendil-works/pi-coding-agent";
3
3
  function resultText(prefix, value) {
4
4
  return `${prefix}\n${JSON.stringify(value, null, 2)}`;
5
5
  }
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
- import { Type } from "@mariozechner/pi-ai";
3
- import { defineTool } from "@mariozechner/pi-coding-agent";
2
+ import { Type } from "@earendil-works/pi-ai";
3
+ import { defineTool } from "@earendil-works/pi-coding-agent";
4
4
  function hashPart(value) {
5
5
  return createHash("sha256").update(value).digest("hex").slice(0, 12);
6
6
  }
@@ -1,8 +1,8 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { extname, isAbsolute, resolve } from "node:path";
4
- import { StringEnum, Type } from "@mariozechner/pi-ai";
5
- import { defineTool } from "@mariozechner/pi-coding-agent";
4
+ import { StringEnum, Type } from "@earendil-works/pi-ai";
5
+ import { defineTool } from "@earendil-works/pi-coding-agent";
6
6
  function resolveCwd(baseCwd, workdir) {
7
7
  if (!workdir || workdir.trim().length === 0)
8
8
  return baseCwd;
@@ -1,9 +1,9 @@
1
1
  import { mkdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { platform, release, arch } from "node:os";
3
3
  import { dirname, extname, isAbsolute, join, resolve } from "node:path";
4
- import { AuthStorage } from "@mariozechner/pi-coding-agent";
5
- import { Type } from "@mariozechner/pi-ai";
6
- import { defineTool } from "@mariozechner/pi-coding-agent";
4
+ import { AuthStorage } from "@earendil-works/pi-coding-agent";
5
+ import { Type } from "@earendil-works/pi-ai";
6
+ import { defineTool } from "@earendil-works/pi-coding-agent";
7
7
  import { getPiboHome } from "../core/pibo-home.js";
8
8
  const OPENAI_CODEX_PROVIDER = "openai-codex";
9
9
  const DEFAULT_CODEX_BACKEND_BASE_URL = "https://chatgpt.com/backend-api";
@@ -1,5 +1,5 @@
1
- import { StringEnum, Type } from "@mariozechner/pi-ai";
2
- import { defineTool } from "@mariozechner/pi-coding-agent";
1
+ import { StringEnum, Type } from "@earendil-works/pi-ai";
2
+ import { defineTool } from "@earendil-works/pi-coding-agent";
3
3
  export function createRuntimeToolProfile() {
4
4
  return {
5
5
  name: "runtime",
@@ -1,5 +1,5 @@
1
- import { StringEnum, Type } from "@mariozechner/pi-ai";
2
- import { defineTool } from "@mariozechner/pi-coding-agent";
1
+ import { StringEnum, Type } from "@earendil-works/pi-ai";
2
+ import { defineTool } from "@earendil-works/pi-coding-agent";
3
3
  import { WEB_ANNOTATION_STATUSES, } from "./types.js";
4
4
  import { createDefaultWebAnnotationStore } from "./store.js";
5
5
  import { assertWebAnnotationStatusTransition, sanitizeWebAnnotationText, WEB_ANNOTATION_LIMITS } from "./validation.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pasko70/pibo",
3
- "version": "1.7.8",
3
+ "version": "1.7.10",
4
4
  "type": "module",
5
5
  "imports": {
6
6
  "vscode": "./src/apps/chat-vscode/extension/src/vscode-shim.js"
@@ -52,8 +52,10 @@
52
52
  "prepack": "npm run build"
53
53
  },
54
54
  "dependencies": {
55
- "@mariozechner/pi-ai": "^0.73.1",
56
- "@mariozechner/pi-coding-agent": "^0.73.1",
55
+ "@earendil-works/pi-agent-core": "^0.80.6",
56
+ "@earendil-works/pi-ai": "^0.80.6",
57
+ "@earendil-works/pi-coding-agent": "^0.80.6",
58
+ "@earendil-works/pi-tui": "^0.80.6",
57
59
  "@mdxeditor/editor": "^3.55.0",
58
60
  "@modelcontextprotocol/sdk": "^1.29.0",
59
61
  "@tailwindcss/vite": "^4.2.4",
@@ -90,7 +90,7 @@ import {
90
90
  DefaultResourceLoader,
91
91
  SessionManager,
92
92
  SettingsManager,
93
- } from "@mariozechner/pi-coding-agent";
93
+ } from "@earendil-works/pi-coding-agent";
94
94
 
95
95
  const sessionManager = SessionManager.open(sessionFile, sessionDir, workspaceDir);
96
96
  const settingsManager = SettingsManager.create(workspaceDir, agentDir);