@pasko70/pibo 3.0.1 → 3.1.0
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 +20 -6
- package/compute-image/Dockerfile +47 -0
- package/compute-image/Dockerfile.dockerignore +5 -0
- package/dist/agent-runtime/context-build.js +26 -2
- package/dist/agent-runtime/contract.js +78 -28
- package/dist/agent-runtime/portable-history.js +19 -9
- package/dist/agent-runtime/registry.js +8 -2
- package/dist/agent-runtime/routed-session.js +120 -15
- package/dist/agent-runtimes/codex-native/adapter.js +12 -0
- package/dist/agent-runtimes/codex-native/resource-delivery.js +35 -7
- package/dist/agent-runtimes/omp/adapter.js +3 -7
- package/dist/agent-runtimes/pi/adapter.js +5 -3
- package/dist/agent-runtimes/pi/routed-session.js +59 -12
- package/dist/agent-runtimes/pi/runtime.js +6 -0
- package/dist/apps/chat/agent-profiles.js +31 -2
- package/dist/apps/chat/agent-store.js +98 -6
- package/dist/apps/chat/chat-request-normalizers.js +31 -0
- package/dist/apps/chat/data/chat-data-mappers.js +61 -11
- package/dist/apps/chat/data/project-service.js +119 -15
- package/dist/apps/chat/data/room-service.js +21 -0
- package/dist/apps/chat/data/session-query-service.js +33 -10
- package/dist/apps/chat/data/timeline-query-service.js +60 -35
- package/dist/apps/chat/loop-api.js +10 -2
- package/dist/apps/chat/output-compactor.js +144 -21
- package/dist/apps/chat/output-event-policy.js +179 -2
- package/dist/apps/chat/stream.js +16 -7
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/web-app.js +317 -86
- package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CUQJqggN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-ClUlQWYN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-Djul9BmZ.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-GD0JGdCi.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.js → dist-W5HOqHym.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BcjkX-iP.js +228 -0
- package/dist/apps/chat-ui/assets/index-CmqRSbBU.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-JvrPUGvI.js +43 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/InkSessionApp.js +111 -12
- package/dist/apps/cli-ui/cliSessionsCommand.js +34 -8
- package/dist/cli-session/localSessionSource.js +153 -161
- package/dist/cli.js +7 -11
- package/dist/compute/cli.js +35 -16
- package/dist/compute/docker.js +341 -94
- package/dist/config/config.js +3 -0
- package/dist/core/context-build.js +7 -16
- package/dist/core/output-persistence-retry.js +484 -0
- package/dist/core/output-render-sequence.js +331 -0
- package/dist/core/profiles.js +24 -0
- package/dist/core/provider-recovery.js +7 -1
- package/dist/core/session-model.js +37 -1
- package/dist/core/session-router.js +78 -110
- package/dist/data/ingest-service.js +91 -9
- package/dist/data/navigation-store.js +5 -5
- package/dist/data/pibo-store.js +8 -1
- package/dist/data/schema.js +243 -3
- package/dist/data/session-store.js +3 -3
- package/dist/debug/agents.js +91 -89
- package/dist/debug/index.js +46 -2
- package/dist/debug/pty.js +127 -48
- package/dist/gateway/cli.js +4 -0
- package/dist/gateway/web.js +8 -8
- package/dist/local/client.js +3 -2
- package/dist/loops/channel.js +3 -1
- package/dist/loops/cli.js +2 -1
- package/dist/loops/service.js +90 -11
- package/dist/loops/store.js +100 -74
- package/dist/mcp/agent-context.js +13 -26
- package/dist/mcp/commands/info.js +3 -1
- package/dist/mcp/config-command.js +46 -2
- package/dist/mcp/config.js +18 -4
- package/dist/plugins/context-files-store.js +36 -6
- package/dist/plugins/context-files.js +52 -1
- package/dist/plugins/registry.js +2 -0
- package/dist/plugins/user-profile-resources.js +22 -0
- package/dist/previews/base-url.js +34 -0
- package/dist/previews/config.js +2 -32
- package/dist/ralph/store.js +5 -0
- package/dist/reliability/store.js +378 -106
- package/dist/session-ui/terminalRows.js +8 -4
- package/dist/sessions/pibo-data-store.js +145 -0
- package/dist/sessions/store.js +42 -0
- package/dist/setup/cli.js +422 -6
- package/dist/setup/installation-profiles.js +464 -0
- package/dist/shared/deterministic-digest.js +94 -0
- package/dist/shared/trace-engine.js +54 -0
- package/dist/shared/trace-event-projection.js +124 -67
- package/dist/shared/trace-history.js +47 -12
- package/dist/shared/trace-live-reducer.js +23 -4
- package/dist/shared/trace-nodes.js +17 -28
- package/dist/shared/trace-order.js +41 -5
- package/dist/shared/trace-page-merge.js +44 -14
- package/dist/shared/trace-patch-nodes.js +2 -0
- package/dist/shared/trace-subagent-links.js +4 -1
- package/dist/shared/trace-tool-identity.js +15 -8
- package/dist/signals/projector.js +28 -6
- package/dist/subagents/observation-query.js +121 -0
- package/dist/subagents/tool.js +7 -3
- package/dist/tools/agent-browser-leases.js +58 -23
- package/dist/tools/agent-browser-wrapper.js +1 -1
- package/dist/tools/browser-use-leases.js +129 -16
- package/dist/tools/browser-use-wrapper.js +1 -1
- package/dist/tools/index.js +27 -11
- package/dist/tools/python-runtime.js +10 -3
- package/dist/tools/registry.js +1 -1
- package/dist/tools/runtime/node-backend.js +16 -5
- package/dist/tools/runtime/node-worker-source.js +112 -33
- package/dist/tools/runtime/python-backend.js +16 -5
- package/dist/tools/runtime/python-worker-source.js +167 -16
- package/dist/tools/runtime/registry.js +23 -10
- package/dist/user-skills/store.js +0 -1
- package/docs/ops/vscode-extension-release.md +9 -1
- package/npm-shrinkwrap.json +9 -2
- package/package.json +9 -2
- package/scripts/docker-entrypoint.sh +46 -0
- package/scripts/prepare-agent-browser-wrapper.sh +70 -0
- package/scripts/prepare-browser-use-wrapper.sh +255 -0
- package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +0 -228
- package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-Dtw2Z7jz.js +0 -43
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.1.vsix +0 -0
package/dist/loops/store.js
CHANGED
|
@@ -5,6 +5,12 @@ import { DatabaseSync } from 'node:sqlite';
|
|
|
5
5
|
import { piboHomePath } from '../core/pibo-home.js';
|
|
6
6
|
import { isPiboThinkingLevel } from '../core/thinking.js';
|
|
7
7
|
import { addLoopAssistantUsage, newGoalTokenAccounting, normalizeLoopTokenAccounting } from './accounting.js';
|
|
8
|
+
export class PiboLoopActiveRunModeChangeError extends Error {
|
|
9
|
+
constructor() {
|
|
10
|
+
super('Loop mode cannot be changed while a Loop run is active');
|
|
11
|
+
this.name = 'PiboLoopActiveRunModeChangeError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
8
14
|
function nowIso(now = new Date()) { return now.toISOString(); }
|
|
9
15
|
function parseJson(json) { return JSON.parse(json); }
|
|
10
16
|
function defaultName(prompt) { const normalized = prompt.replace(/\s+/g, ' ').trim(); return normalized ? normalized.slice(0, 80) : 'Loop job'; }
|
|
@@ -507,48 +513,56 @@ export class PiboLoopStore {
|
|
|
507
513
|
return this.db.prepare(`SELECT * FROM pibo_ralph_jobs ${where} ORDER BY updated_at DESC, id ASC`).all(...values).map(jobFromRow);
|
|
508
514
|
}
|
|
509
515
|
updateJob(id, patch, now = new Date()) {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
fastMode: hasOwn(patch, 'fastMode') ? patch.fastMode : existing.fastMode,
|
|
517
|
-
});
|
|
518
|
-
const stopPolicy = hasOwn(patch, 'stopPolicy') ? normalizeLoopStopPolicy(patch.stopPolicy ?? undefined) : existing.stopPolicy;
|
|
519
|
-
const target = patch.target ? normalizeLoopTarget(patch.target) : existing.target;
|
|
520
|
-
const mode = patch.mode !== undefined ? normalizeLoopMode(patch.mode) : existing.mode;
|
|
521
|
-
const enabled = patch.enabled ?? existing.enabled;
|
|
522
|
-
let state = mode === existing.mode
|
|
523
|
-
? { ...existing.state }
|
|
524
|
-
: { completedIterations: existing.state.completedIterations ?? 0, ...(mode === 'goal' ? { goalStatus: enabled ? 'active' : 'paused', tokenAccounting: newGoalTokenAccounting(), tokensUsed: 0, activeTimeSeconds: 0, ...(enabled ? { goalStartedAt: nowIso(now) } : {}) } : {}) };
|
|
525
|
-
if (mode === 'goal' && patch.enabled !== undefined) {
|
|
526
|
-
const currentGoalStatus = goalStatus({ mode, enabled: existing.enabled, state: existing.state }) ?? 'paused';
|
|
527
|
-
if (patch.enabled) {
|
|
528
|
-
if (currentGoalStatus === 'complete')
|
|
529
|
-
throw new Error('Completed goals cannot be restarted; create a new goal');
|
|
530
|
-
const nextBudget = hasOwn(patch, 'tokenBudget') ? normalizeTokenBudget(patch.tokenBudget ?? undefined) : existing.tokenBudget;
|
|
531
|
-
const nextReserve = hasOwn(patch, 'tokenReserve') ? normalizeTokenReserve(patch.tokenReserve ?? undefined) : existing.tokenReserve;
|
|
532
|
-
if (currentGoalStatus === 'budget_limited' && nextBudget !== undefined && (existing.state.tokensUsed ?? 0) + (nextReserve ?? 0) >= nextBudget)
|
|
533
|
-
throw new Error('Increase or clear the token budget, or lower the token reserve, before resuming this goal');
|
|
534
|
-
state.goalStatus = 'active';
|
|
535
|
-
state.goalStartedAt ??= nowIso(now);
|
|
536
|
-
delete state.goalEndedAt;
|
|
537
|
-
state.stopRequestedAt = undefined;
|
|
538
|
-
state.cancelRequestedAt = undefined;
|
|
539
|
-
state.lastFailure = undefined;
|
|
540
|
-
state.nextAttemptAt = undefined;
|
|
541
|
-
state.retryBackoffMs = undefined;
|
|
542
|
-
state.consecutiveErrors = 0;
|
|
516
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
517
|
+
try {
|
|
518
|
+
const existing = this.getJob(id);
|
|
519
|
+
if (!existing) {
|
|
520
|
+
this.db.exec('COMMIT');
|
|
521
|
+
return undefined;
|
|
543
522
|
}
|
|
544
|
-
|
|
545
|
-
|
|
523
|
+
const runtimeOptions = normalizeRuntimeOptions({
|
|
524
|
+
modelOverride: hasOwn(patch, 'modelOverride') ? patch.modelOverride : existing.modelOverride,
|
|
525
|
+
thinkingLevel: hasOwn(patch, 'thinkingLevel') ? patch.thinkingLevel : existing.thinkingLevel,
|
|
526
|
+
fastMode: hasOwn(patch, 'fastMode') ? patch.fastMode : existing.fastMode,
|
|
527
|
+
});
|
|
528
|
+
const stopPolicy = hasOwn(patch, 'stopPolicy') ? normalizeLoopStopPolicy(patch.stopPolicy ?? undefined) : existing.stopPolicy;
|
|
529
|
+
const target = patch.target ? normalizeLoopTarget(patch.target) : existing.target;
|
|
530
|
+
const mode = patch.mode !== undefined ? normalizeLoopMode(patch.mode) : existing.mode;
|
|
531
|
+
if (mode !== existing.mode && this.db.prepare("SELECT 1 FROM pibo_ralph_runs WHERE job_id = ? AND status = 'running' LIMIT 1").get(id))
|
|
532
|
+
throw new PiboLoopActiveRunModeChangeError();
|
|
533
|
+
const enabled = patch.enabled ?? existing.enabled;
|
|
534
|
+
let state = mode === existing.mode
|
|
535
|
+
? { ...existing.state }
|
|
536
|
+
: { completedIterations: existing.state.completedIterations ?? 0, ...(mode === 'goal' ? { goalStatus: enabled ? 'active' : 'paused', tokenAccounting: newGoalTokenAccounting(), tokensUsed: 0, activeTimeSeconds: 0, ...(enabled ? { goalStartedAt: nowIso(now) } : {}) } : {}) };
|
|
537
|
+
if (mode === 'goal' && patch.enabled !== undefined) {
|
|
538
|
+
const currentGoalStatus = goalStatus({ mode, enabled: existing.enabled, state: existing.state }) ?? 'paused';
|
|
539
|
+
if (patch.enabled) {
|
|
540
|
+
if (isTerminalGoalStatus(currentGoalStatus))
|
|
541
|
+
throw new Error('Terminal Goals cannot be restarted; use the confirmed Goal reopen operation');
|
|
542
|
+
state.goalStatus = 'active';
|
|
543
|
+
state.goalStartedAt ??= nowIso(now);
|
|
544
|
+
delete state.goalEndedAt;
|
|
545
|
+
state.stopRequestedAt = undefined;
|
|
546
|
+
state.cancelRequestedAt = undefined;
|
|
547
|
+
state.lastFailure = undefined;
|
|
548
|
+
state.nextAttemptAt = undefined;
|
|
549
|
+
state.retryBackoffMs = undefined;
|
|
550
|
+
state.consecutiveErrors = 0;
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
state.goalStatus = currentGoalStatus === 'active' ? 'paused' : currentGoalStatus;
|
|
554
|
+
}
|
|
546
555
|
}
|
|
556
|
+
const next = { ...existing, mode, state, name: patch.name !== undefined ? patch.name.trim() : existing.name, description: patch.description !== undefined ? patch.description?.trim() || undefined : existing.description, enabled, target, profile: patch.profile ?? existing.profile, prompt: patch.prompt ?? existing.prompt, maxIterations: hasOwn(patch, 'maxIterations') ? normalizeMaxIterations(patch.maxIterations ?? undefined) : existing.maxIterations, tokenBudget: mode === 'ralph' ? undefined : hasOwn(patch, 'tokenBudget') ? normalizeTokenBudget(patch.tokenBudget ?? undefined) : existing.tokenBudget, tokenReserve: mode === 'ralph' || (hasOwn(patch, 'tokenBudget') && patch.tokenBudget === null && !hasOwn(patch, 'tokenReserve')) ? undefined : hasOwn(patch, 'tokenReserve') ? normalizeTokenReserve(patch.tokenReserve ?? undefined) : existing.tokenReserve, stopPolicy, modelOverride: runtimeOptions.modelOverride, thinkingLevel: runtimeOptions.thinkingLevel, fastMode: runtimeOptions.fastMode, updatedAt: nowIso(now) };
|
|
557
|
+
validateJobInput(next);
|
|
558
|
+
this.writeJob(next);
|
|
559
|
+
this.db.exec('COMMIT');
|
|
560
|
+
return this.getJob(id);
|
|
561
|
+
}
|
|
562
|
+
catch (error) {
|
|
563
|
+
this.db.exec('ROLLBACK');
|
|
564
|
+
throw error;
|
|
547
565
|
}
|
|
548
|
-
const next = { ...existing, mode, state, name: patch.name !== undefined ? patch.name.trim() : existing.name, description: patch.description !== undefined ? patch.description?.trim() || undefined : existing.description, enabled, target, profile: patch.profile ?? existing.profile, prompt: patch.prompt ?? existing.prompt, maxIterations: hasOwn(patch, 'maxIterations') ? normalizeMaxIterations(patch.maxIterations ?? undefined) : existing.maxIterations, tokenBudget: mode === 'ralph' ? undefined : hasOwn(patch, 'tokenBudget') ? normalizeTokenBudget(patch.tokenBudget ?? undefined) : existing.tokenBudget, tokenReserve: mode === 'ralph' || (hasOwn(patch, 'tokenBudget') && patch.tokenBudget === null && !hasOwn(patch, 'tokenReserve')) ? undefined : hasOwn(patch, 'tokenReserve') ? normalizeTokenReserve(patch.tokenReserve ?? undefined) : existing.tokenReserve, stopPolicy, modelOverride: runtimeOptions.modelOverride, thinkingLevel: runtimeOptions.thinkingLevel, fastMode: runtimeOptions.fastMode, updatedAt: nowIso(now) };
|
|
549
|
-
validateJobInput(next);
|
|
550
|
-
this.writeJob(next);
|
|
551
|
-
return this.getJob(id);
|
|
552
566
|
}
|
|
553
567
|
updateJobResources(id, resources, now = new Date()) {
|
|
554
568
|
const existing = this.getJob(id);
|
|
@@ -621,6 +635,7 @@ export class PiboLoopStore {
|
|
|
621
635
|
return Number(result.changes ?? 0) > 0 ? this.getRunByMessageEventId(eventId) : undefined;
|
|
622
636
|
}
|
|
623
637
|
reserveRun(id, now = new Date()) { this.updateJob(id, { enabled: true }, now); return this.reserveJob(id, now); }
|
|
638
|
+
reserveAdmittedRun(id, now = new Date()) { return this.reserveJob(id, now, true); }
|
|
624
639
|
reserveDueRuns(limit, now = new Date()) {
|
|
625
640
|
const rows = this.db.prepare('SELECT * FROM pibo_ralph_jobs WHERE enabled = 1 ORDER BY updated_at ASC').all();
|
|
626
641
|
const result = [];
|
|
@@ -667,41 +682,47 @@ export class PiboLoopStore {
|
|
|
667
682
|
}
|
|
668
683
|
completeRun(input, now = new Date()) {
|
|
669
684
|
const timestamp = nowIso(now);
|
|
670
|
-
const job = this.getJob(input.jobId);
|
|
671
|
-
if (!job)
|
|
672
|
-
return;
|
|
673
|
-
const completedIterations = (job.state.completedIterations ?? 0) + 1;
|
|
674
|
-
const reachedMaxIterations = job.maxIterations !== undefined && completedIterations >= job.maxIterations;
|
|
675
|
-
const currentGoalStatus = goalStatus(job);
|
|
676
|
-
const nextGoalStatus = job.mode === 'goal'
|
|
677
|
-
? isTerminalGoalStatus(currentGoalStatus) || currentGoalStatus === 'paused'
|
|
678
|
-
? currentGoalStatus
|
|
679
|
-
: input.goalStatus ?? currentGoalStatus
|
|
680
|
-
: undefined;
|
|
681
|
-
const terminalGoalStatus = job.mode === 'goal' && isTerminalGoalStatus(nextGoalStatus);
|
|
682
|
-
const shouldDisable = terminalGoalStatus || reachedMaxIterations || input.stopAfterRun === true || input.stopEvaluation?.finalAction === 'stop-after-run' || input.stopEvaluation?.finalAction === 'cancel-current-run';
|
|
683
|
-
const state = {
|
|
684
|
-
...job.state,
|
|
685
|
-
...(job.mode === 'goal' ? { activeTimeRunningAt: null } : {}),
|
|
686
|
-
runningAt: undefined,
|
|
687
|
-
completedIterations,
|
|
688
|
-
lastRunAt: timestamp,
|
|
689
|
-
lastRunId: input.runId,
|
|
690
|
-
lastStatus: input.status === 'error' ? 'error' : input.status === 'cancelled' ? 'cancelled' : 'ok',
|
|
691
|
-
lastError: input.error,
|
|
692
|
-
lastFailure: input.status === 'error' ? input.failure : undefined,
|
|
693
|
-
nextAttemptAt: input.status === 'error' ? input.failure?.nextAttemptAt : undefined,
|
|
694
|
-
retryBackoffMs: input.status === 'error' ? input.failure?.retryBackoffMs : undefined,
|
|
695
|
-
lastPiboSessionId: input.piboSessionId ?? job.state.lastPiboSessionId,
|
|
696
|
-
consecutiveErrors: input.status === 'error' ? (job.state.consecutiveErrors ?? 0) + 1 : 0,
|
|
697
|
-
conditionStates: input.conditionStates ?? job.state.conditionStates,
|
|
698
|
-
lastStopEvaluation: input.stopEvaluation ?? job.state.lastStopEvaluation,
|
|
699
|
-
...(nextGoalStatus ? { goalStatus: nextGoalStatus } : {}),
|
|
700
|
-
...(terminalGoalStatus ? { goalEndedAt: job.state.goalEndedAt ?? timestamp } : {}),
|
|
701
|
-
};
|
|
702
685
|
this.db.exec('BEGIN IMMEDIATE');
|
|
703
686
|
try {
|
|
704
|
-
this.db.prepare("UPDATE pibo_ralph_runs SET status = ?, pibo_session_id = COALESCE(?, pibo_session_id), reason = ?, error = ?, error_details_json = ?, message_state = CASE WHEN message_state = 'invalidated' THEN message_state ELSE 'finished' END, completed_at = ?, updated_at = ? WHERE id = ?").run(input.status, input.piboSessionId ?? null, input.reason ?? input.stopEvaluation?.reason ?? null, input.error ?? null, sessionErrorDetailsJson(input.errorDetails), timestamp, timestamp, input.runId);
|
|
687
|
+
const result = this.db.prepare("UPDATE pibo_ralph_runs SET status = ?, pibo_session_id = COALESCE(?, pibo_session_id), reason = ?, error = ?, error_details_json = ?, message_state = CASE WHEN message_state = 'invalidated' THEN message_state ELSE 'finished' END, completed_at = ?, updated_at = ? WHERE id = ? AND job_id = ? AND status = 'running'").run(input.status, input.piboSessionId ?? null, input.reason ?? input.stopEvaluation?.reason ?? null, input.error ?? null, sessionErrorDetailsJson(input.errorDetails), timestamp, timestamp, input.runId, input.jobId);
|
|
688
|
+
if (Number(result.changes ?? 0) === 0) {
|
|
689
|
+
this.db.exec('COMMIT');
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const job = this.getJob(input.jobId);
|
|
693
|
+
if (!job || !job.state.runningAt || job.state.lastRunId !== input.runId) {
|
|
694
|
+
this.db.exec('COMMIT');
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
const completedIterations = (job.state.completedIterations ?? 0) + 1;
|
|
698
|
+
const reachedMaxIterations = job.maxIterations !== undefined && completedIterations >= job.maxIterations;
|
|
699
|
+
const currentGoalStatus = goalStatus(job);
|
|
700
|
+
const nextGoalStatus = job.mode === 'goal'
|
|
701
|
+
? isTerminalGoalStatus(currentGoalStatus) || currentGoalStatus === 'paused'
|
|
702
|
+
? currentGoalStatus
|
|
703
|
+
: input.goalStatus ?? currentGoalStatus
|
|
704
|
+
: undefined;
|
|
705
|
+
const terminalGoalStatus = job.mode === 'goal' && isTerminalGoalStatus(nextGoalStatus);
|
|
706
|
+
const shouldDisable = terminalGoalStatus || reachedMaxIterations || input.stopAfterRun === true || input.stopEvaluation?.finalAction === 'stop-after-run' || input.stopEvaluation?.finalAction === 'cancel-current-run';
|
|
707
|
+
const state = {
|
|
708
|
+
...job.state,
|
|
709
|
+
...(job.mode === 'goal' ? { activeTimeRunningAt: null } : {}),
|
|
710
|
+
runningAt: undefined,
|
|
711
|
+
completedIterations,
|
|
712
|
+
lastRunAt: timestamp,
|
|
713
|
+
lastRunId: input.runId,
|
|
714
|
+
lastStatus: input.status === 'error' ? 'error' : input.status === 'cancelled' ? 'cancelled' : 'ok',
|
|
715
|
+
lastError: input.error,
|
|
716
|
+
lastFailure: input.status === 'error' ? input.failure : undefined,
|
|
717
|
+
nextAttemptAt: input.status === 'error' ? input.failure?.nextAttemptAt : undefined,
|
|
718
|
+
retryBackoffMs: input.status === 'error' ? input.failure?.retryBackoffMs : undefined,
|
|
719
|
+
lastPiboSessionId: input.piboSessionId ?? job.state.lastPiboSessionId,
|
|
720
|
+
consecutiveErrors: input.status === 'error' ? (job.state.consecutiveErrors ?? 0) + 1 : 0,
|
|
721
|
+
conditionStates: input.conditionStates ?? job.state.conditionStates,
|
|
722
|
+
lastStopEvaluation: input.stopEvaluation ?? job.state.lastStopEvaluation,
|
|
723
|
+
...(nextGoalStatus ? { goalStatus: nextGoalStatus } : {}),
|
|
724
|
+
...(terminalGoalStatus ? { goalEndedAt: job.state.goalEndedAt ?? timestamp } : {}),
|
|
725
|
+
};
|
|
705
726
|
this.db.prepare('UPDATE pibo_ralph_jobs SET enabled = ?, state_json = ?, updated_at = ? WHERE id = ?').run(shouldDisable ? 0 : job.enabled ? 1 : 0, JSON.stringify(state), timestamp, job.id);
|
|
706
727
|
this.db.exec('COMMIT');
|
|
707
728
|
}
|
|
@@ -768,12 +789,17 @@ export class PiboLoopStore {
|
|
|
768
789
|
this.updateRunResources({ jobId: job.id, runId, resources: nextResources });
|
|
769
790
|
this.updateJobResources(job.id, nextResources);
|
|
770
791
|
}
|
|
771
|
-
reserveJob(id, now = new Date()) {
|
|
792
|
+
reserveJob(id, now = new Date(), requireAdmission = false) {
|
|
772
793
|
const timestamp = nowIso(now);
|
|
773
794
|
this.db.exec('BEGIN IMMEDIATE');
|
|
774
795
|
try {
|
|
775
796
|
const job = this.getJob(id);
|
|
776
|
-
if (!job || !job.enabled || job.state.runningAt) {
|
|
797
|
+
if (!job || !job.enabled || job.state.runningAt || (requireAdmission && job.state.cancelRequestedAt !== undefined)) {
|
|
798
|
+
this.db.exec('COMMIT');
|
|
799
|
+
return undefined;
|
|
800
|
+
}
|
|
801
|
+
if (job.maxIterations !== undefined && (job.state.completedIterations ?? 0) >= job.maxIterations) {
|
|
802
|
+
this.db.prepare('UPDATE pibo_ralph_jobs SET enabled = 0, updated_at = ? WHERE id = ?').run(timestamp, job.id);
|
|
777
803
|
this.db.exec('COMMIT');
|
|
778
804
|
return undefined;
|
|
779
805
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { isHttpServer, loadConfigUnresolved, resolveMcpServerConfigSource, } from './config.js';
|
|
3
3
|
import { ErrorCode, formatCliError } from './errors.js';
|
|
4
4
|
export const MCP_SERVER_DESCRIPTION_MAX_LENGTH = 480;
|
|
5
5
|
export const ENABLED_MCP_SERVERS_CONTEXT_PATH = '.pibo/context/enabled-mcp-servers.md';
|
|
@@ -34,15 +34,19 @@ export async function listMcpServerInfos(configPath) {
|
|
|
34
34
|
}
|
|
35
35
|
export async function setMcpServerDescription(serverName, descriptionInput, configPath) {
|
|
36
36
|
const description = normalizeMcpServerDescription(descriptionInput);
|
|
37
|
-
const path = await
|
|
38
|
-
|
|
39
|
-
const server = config.mcpServers[serverName];
|
|
40
|
-
if (!server) {
|
|
37
|
+
const { path, config, server } = await resolveMcpServerConfigSource(serverName, configPath);
|
|
38
|
+
if (server.pibo?.descriptionSource === 'registry') {
|
|
41
39
|
throw new Error(formatCliError({
|
|
42
40
|
code: ErrorCode.CLIENT_ERROR,
|
|
43
|
-
type: '
|
|
44
|
-
message: `Server "${serverName}"
|
|
45
|
-
|
|
41
|
+
type: 'MCP_DESCRIPTION_READ_ONLY',
|
|
42
|
+
message: `Server "${serverName}" description is read-only`,
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
if (process.platform !== 'win32' && ((await stat(path)).mode & 0o222) === 0) {
|
|
46
|
+
throw new Error(formatCliError({
|
|
47
|
+
code: ErrorCode.CLIENT_ERROR,
|
|
48
|
+
type: 'MCP_CONFIG_READ_ONLY',
|
|
49
|
+
message: `Server "${serverName}" config is read-only`,
|
|
46
50
|
}));
|
|
47
51
|
}
|
|
48
52
|
server.pibo = {
|
|
@@ -91,23 +95,6 @@ export function getMcpAgentContextFileFromConfig(selectedServerNames, config) {
|
|
|
91
95
|
].join('\n').trimEnd(),
|
|
92
96
|
};
|
|
93
97
|
}
|
|
94
|
-
async function readMcpConfig(path) {
|
|
95
|
-
const content = await readFile(path, 'utf-8');
|
|
96
|
-
const parsed = JSON.parse(content);
|
|
97
|
-
if (!parsed ||
|
|
98
|
-
typeof parsed !== 'object' ||
|
|
99
|
-
!('mcpServers' in parsed) ||
|
|
100
|
-
typeof parsed.mcpServers !== 'object' ||
|
|
101
|
-
parsed.mcpServers === null) {
|
|
102
|
-
throw new Error(formatCliError({
|
|
103
|
-
code: ErrorCode.CLIENT_ERROR,
|
|
104
|
-
type: 'CONFIG_MISSING_FIELD',
|
|
105
|
-
message: 'Config file missing required "mcpServers" object',
|
|
106
|
-
details: `File: ${path}`,
|
|
107
|
-
}));
|
|
108
|
-
}
|
|
109
|
-
return parsed;
|
|
110
|
-
}
|
|
111
98
|
function mcpServerInfoFromConfig(name, server) {
|
|
112
99
|
const metadata = normalizeMetadata(server.pibo);
|
|
113
100
|
const description = metadata.description;
|
|
@@ -28,7 +28,9 @@ export async function infoCommand(options) {
|
|
|
28
28
|
process.exit(ErrorCode.CLIENT_ERROR);
|
|
29
29
|
}
|
|
30
30
|
const { server: serverName, tool: toolName } = parseTarget(options.target);
|
|
31
|
-
const serverConfig = config.mcpServers
|
|
31
|
+
const serverConfig = Object.hasOwn(config.mcpServers, serverName)
|
|
32
|
+
? config.mcpServers[serverName]
|
|
33
|
+
: undefined;
|
|
32
34
|
if (!serverConfig) {
|
|
33
35
|
const available = Object.keys(config.mcpServers);
|
|
34
36
|
const serverList = available.length > 0 ? available.join(', ') : '(none)';
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { CustomAgentStore } from '../apps/chat/agent-store.js';
|
|
5
|
+
import { piboHomePath } from '../core/pibo-home.js';
|
|
6
|
+
import { ensureConfigExists, findConfigPath, getConfigSearchPaths, getPreferredConfigPath, } from './config.js';
|
|
4
7
|
import { ErrorCode, formatCliError } from './errors.js';
|
|
5
8
|
import { setMcpServerDescription } from './agent-context.js';
|
|
6
9
|
const EXAMPLE_CONFIG = {
|
|
@@ -97,6 +100,35 @@ async function readRawConfig(path) {
|
|
|
97
100
|
async function writeRawConfig(path, config) {
|
|
98
101
|
await writeFile(path, `${JSON.stringify(config, null, 2)}\n`);
|
|
99
102
|
}
|
|
103
|
+
async function mergedConfigRetainsServer(name, updatedPath, updatedConfig, explicitPath) {
|
|
104
|
+
const resolvedUpdatedPath = resolve(updatedPath);
|
|
105
|
+
for (const sourcePath of getConfigSearchPaths(explicitPath)) {
|
|
106
|
+
if (!existsSync(sourcePath))
|
|
107
|
+
continue;
|
|
108
|
+
const sourceConfig = resolve(sourcePath) === resolvedUpdatedPath
|
|
109
|
+
? updatedConfig
|
|
110
|
+
: await readRawConfig(sourcePath);
|
|
111
|
+
if (Object.hasOwn(sourceConfig.mcpServers, name))
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
function customAgentsSelectingMcpServer(name) {
|
|
117
|
+
const storePath = piboHomePath('chat-agents.sqlite');
|
|
118
|
+
if (!existsSync(storePath))
|
|
119
|
+
return [];
|
|
120
|
+
const store = new CustomAgentStore(storePath);
|
|
121
|
+
try {
|
|
122
|
+
return store
|
|
123
|
+
.list({ includeArchived: true })
|
|
124
|
+
.filter((agent) => agent.mcpServers.includes(name))
|
|
125
|
+
.map((agent) => agent.profileName)
|
|
126
|
+
.sort((left, right) => left.localeCompare(right));
|
|
127
|
+
}
|
|
128
|
+
finally {
|
|
129
|
+
store.close();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
100
132
|
function parseServerConfig(input) {
|
|
101
133
|
let parsed;
|
|
102
134
|
try {
|
|
@@ -205,7 +237,7 @@ export async function configCommand(options) {
|
|
|
205
237
|
suggestion: 'Example: pibo mcp config remove filesystem',
|
|
206
238
|
}));
|
|
207
239
|
}
|
|
208
|
-
if (!(options.name
|
|
240
|
+
if (!Object.hasOwn(config.mcpServers, options.name)) {
|
|
209
241
|
throw new Error(formatCliError({
|
|
210
242
|
code: ErrorCode.CLIENT_ERROR,
|
|
211
243
|
type: 'SERVER_NOT_FOUND',
|
|
@@ -214,6 +246,18 @@ export async function configCommand(options) {
|
|
|
214
246
|
}));
|
|
215
247
|
}
|
|
216
248
|
delete config.mcpServers[options.name];
|
|
249
|
+
if (!(await mergedConfigRetainsServer(options.name, path, config, options.configPath))) {
|
|
250
|
+
const affectedAgents = customAgentsSelectingMcpServer(options.name);
|
|
251
|
+
if (affectedAgents.length > 0) {
|
|
252
|
+
throw new Error(formatCliError({
|
|
253
|
+
code: ErrorCode.CLIENT_ERROR,
|
|
254
|
+
type: 'MCP_SERVER_IN_USE',
|
|
255
|
+
message: `MCP server "${options.name}" is selected by custom agents`,
|
|
256
|
+
details: `Custom agents: ${affectedAgents.join(', ')}`,
|
|
257
|
+
suggestion: 'Update those agents to stop selecting this MCP server, then retry the removal.',
|
|
258
|
+
}));
|
|
259
|
+
}
|
|
260
|
+
}
|
|
217
261
|
await writeRawConfig(path, config);
|
|
218
262
|
console.log(`Removed MCP server "${options.name}" from ${path}`);
|
|
219
263
|
}
|
package/dist/mcp/config.js
CHANGED
|
@@ -496,7 +496,7 @@ export async function loadConfigUnresolved(explicitPath) {
|
|
|
496
496
|
for (const configPath of existingPaths) {
|
|
497
497
|
const config = await readRawConfig(configPath);
|
|
498
498
|
for (const [serverName, serverConfig] of Object.entries(config.mcpServers)) {
|
|
499
|
-
if (!(
|
|
499
|
+
if (!Object.hasOwn(merged.mcpServers, serverName)) {
|
|
500
500
|
merged.mcpServers[serverName] = serverConfig;
|
|
501
501
|
}
|
|
502
502
|
}
|
|
@@ -506,6 +506,21 @@ export async function loadConfigUnresolved(explicitPath) {
|
|
|
506
506
|
}
|
|
507
507
|
return merged;
|
|
508
508
|
}
|
|
509
|
+
/**
|
|
510
|
+
* Resolve the highest-priority config file that contributes a merged server.
|
|
511
|
+
*/
|
|
512
|
+
export async function resolveMcpServerConfigSource(serverName, explicitPath) {
|
|
513
|
+
const merged = await loadConfigUnresolved(explicitPath);
|
|
514
|
+
for (const configPath of getConfigSearchPaths(explicitPath)) {
|
|
515
|
+
if (!existsSync(configPath))
|
|
516
|
+
continue;
|
|
517
|
+
const config = await readRawConfig(configPath);
|
|
518
|
+
if (Object.hasOwn(config.mcpServers, serverName)) {
|
|
519
|
+
return { path: configPath, config, server: config.mcpServers[serverName] };
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
throw new Error(formatCliError(serverNotFoundError(serverName, Object.keys(merged.mcpServers))));
|
|
523
|
+
}
|
|
509
524
|
export async function loadConfig(explicitPath) {
|
|
510
525
|
return substituteEnvVarsInObject(await loadConfigUnresolved(explicitPath));
|
|
511
526
|
}
|
|
@@ -539,12 +554,11 @@ export function formatConfigSourceSummaries(summaries) {
|
|
|
539
554
|
* Get a specific server config by name
|
|
540
555
|
*/
|
|
541
556
|
export function getServerConfig(config, serverName) {
|
|
542
|
-
|
|
543
|
-
if (!server) {
|
|
557
|
+
if (!Object.hasOwn(config.mcpServers, serverName)) {
|
|
544
558
|
const available = Object.keys(config.mcpServers);
|
|
545
559
|
throw new Error(formatCliError(serverNotFoundError(serverName, available)));
|
|
546
560
|
}
|
|
547
|
-
return
|
|
561
|
+
return config.mcpServers[serverName];
|
|
548
562
|
}
|
|
549
563
|
/**
|
|
550
564
|
* List all server names
|
|
@@ -101,6 +101,36 @@ function revisionRowToRecord(row) {
|
|
|
101
101
|
...(row.actor_id ? { actorId: row.actor_id } : {}),
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
+
export function readContextFileCatalog(path) {
|
|
105
|
+
const resolvedPath = resolve(path);
|
|
106
|
+
if (!existsSync(resolvedPath))
|
|
107
|
+
return [];
|
|
108
|
+
const db = new DatabaseSync(resolvedPath, { readOnly: true });
|
|
109
|
+
try {
|
|
110
|
+
const table = db.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'context_files'").get();
|
|
111
|
+
if (!table)
|
|
112
|
+
return [];
|
|
113
|
+
const columns = new Set(db.prepare("PRAGMA table_info(context_files)").all().map((column) => column.name));
|
|
114
|
+
const required = ["key", "label", "managed_path", "scope", "agent_profile_name"];
|
|
115
|
+
if (!required.every((column) => columns.has(column)))
|
|
116
|
+
return [];
|
|
117
|
+
const rows = db.prepare(`
|
|
118
|
+
SELECT key, label, managed_path, scope, agent_profile_name
|
|
119
|
+
FROM context_files
|
|
120
|
+
ORDER BY updated_at DESC
|
|
121
|
+
`).all();
|
|
122
|
+
return rows.map((row) => ({
|
|
123
|
+
key: row.key,
|
|
124
|
+
label: row.label,
|
|
125
|
+
managedPath: row.managed_path,
|
|
126
|
+
scope: row.scope,
|
|
127
|
+
...(row.agent_profile_name ? { agentProfileName: row.agent_profile_name } : {}),
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
db.close();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
104
134
|
export class ContextFileMetadataStore {
|
|
105
135
|
legacyStorePath;
|
|
106
136
|
db;
|
|
@@ -415,6 +445,12 @@ export class ContextFileMetadataStore {
|
|
|
415
445
|
WHERE key = ?
|
|
416
446
|
`).run(file.workingContent, file.sourceContent ?? null, file.key);
|
|
417
447
|
}
|
|
448
|
+
for (const file of recovered) {
|
|
449
|
+
if (!file.restoreManagedFile)
|
|
450
|
+
continue;
|
|
451
|
+
mkdirSync(dirname(file.managedPath), { recursive: true });
|
|
452
|
+
writeFileSync(file.managedPath, file.workingContent, "utf8");
|
|
453
|
+
}
|
|
418
454
|
const migratedAt = new Date().toISOString();
|
|
419
455
|
this.db.prepare(`
|
|
420
456
|
INSERT INTO context_file_store_meta (key, value) VALUES (?, ?)
|
|
@@ -430,12 +466,6 @@ export class ContextFileMetadataStore {
|
|
|
430
466
|
this.db.exec("ROLLBACK");
|
|
431
467
|
throw error;
|
|
432
468
|
}
|
|
433
|
-
for (const file of recovered) {
|
|
434
|
-
if (!file.restoreManagedFile)
|
|
435
|
-
continue;
|
|
436
|
-
mkdirSync(dirname(file.managedPath), { recursive: true });
|
|
437
|
-
writeFileSync(file.managedPath, file.workingContent, "utf8");
|
|
438
|
-
}
|
|
439
469
|
}
|
|
440
470
|
migrateLegacyStore() {
|
|
441
471
|
if (!this.legacyStorePath)
|
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { readFallbackPidFile, readPidFile } from "../gateway/pidfile.js";
|
|
7
7
|
import { PiboWebHttpError, readJsonBody, responseHtml, responseJson } from "../web/http.js";
|
|
8
8
|
import { definePiboPlugin } from "./registry.js";
|
|
9
|
-
import { buildContextFileDiff, ContextFileMetadataStore, hashContextFileContent, } from "./context-files-store.js";
|
|
9
|
+
import { buildContextFileDiff, ContextFileMetadataStore, hashContextFileContent, readContextFileCatalog, } from "./context-files-store.js";
|
|
10
10
|
export const CONTEXT_FILES_APP_NAME = "pibo.context-files";
|
|
11
11
|
export const CONTEXT_FILES_MOUNT_PATH = "/apps/context-files";
|
|
12
12
|
export const CONTEXT_FILES_API_PREFIX = "/api/context-files";
|
|
@@ -1028,6 +1028,57 @@ function createContextFilesWebApp(service) {
|
|
|
1028
1028
|
},
|
|
1029
1029
|
};
|
|
1030
1030
|
}
|
|
1031
|
+
export function createPiboContextFilesCatalogPlugin(options = {}) {
|
|
1032
|
+
const paths = resolveContextFilesPaths(options);
|
|
1033
|
+
return definePiboPlugin({
|
|
1034
|
+
id: "pibo.context-files-catalog",
|
|
1035
|
+
name: "Pibo Context Files Catalog",
|
|
1036
|
+
register(api) {
|
|
1037
|
+
const registeredPaths = new Set();
|
|
1038
|
+
const registeredKeys = new Set();
|
|
1039
|
+
try {
|
|
1040
|
+
for (const file of readContextFileCatalog(paths.metadataPath)) {
|
|
1041
|
+
api.upsertContextFile(profileForManaged(file));
|
|
1042
|
+
registeredPaths.add(resolve(file.managedPath));
|
|
1043
|
+
registeredKeys.add(file.key);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
catch (error) {
|
|
1047
|
+
console.warn(`[pibo] Skipping managed context-file catalog registration: ${error instanceof Error ? error.message : String(error)}`);
|
|
1048
|
+
}
|
|
1049
|
+
let entries = [];
|
|
1050
|
+
try {
|
|
1051
|
+
entries = readdirSync(paths.globalDir, { withFileTypes: true });
|
|
1052
|
+
}
|
|
1053
|
+
catch (error) {
|
|
1054
|
+
const code = error && typeof error === "object" && "code" in error ? error.code : undefined;
|
|
1055
|
+
if (code !== "ENOENT") {
|
|
1056
|
+
console.warn(`[pibo] Skipping global context-file discovery: ${error instanceof Error ? error.message : String(error)}`);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
const usedKeys = new Set(registeredKeys);
|
|
1060
|
+
for (const entry of entries) {
|
|
1061
|
+
if (!entry.isFile())
|
|
1062
|
+
continue;
|
|
1063
|
+
const extension = extname(entry.name).toLowerCase();
|
|
1064
|
+
if (extension !== ".md" && extension !== ".markdown")
|
|
1065
|
+
continue;
|
|
1066
|
+
const path = resolve(paths.globalDir, entry.name);
|
|
1067
|
+
if (registeredPaths.has(path))
|
|
1068
|
+
continue;
|
|
1069
|
+
const key = uniqueKey(`ctx:${slugSegment(entry.name.slice(0, -extension.length) || entry.name)}`, usedKeys);
|
|
1070
|
+
usedKeys.add(key);
|
|
1071
|
+
api.upsertContextFile({
|
|
1072
|
+
key,
|
|
1073
|
+
label: labelFromManagedPath(entry.name),
|
|
1074
|
+
path,
|
|
1075
|
+
source: "managed",
|
|
1076
|
+
scope: "global",
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
},
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1031
1082
|
export function createPiboContextFilesPlugin(options = {}) {
|
|
1032
1083
|
const paths = resolveContextFilesPaths(options);
|
|
1033
1084
|
return definePiboPlugin({
|
package/dist/plugins/registry.js
CHANGED
|
@@ -521,11 +521,13 @@ export class PiboPluginRegistry {
|
|
|
521
521
|
nativeTools: sessionContext.tools.filter((tool) => tool.enabled !== false).map((tool) => tool.name),
|
|
522
522
|
skills: sessionContext.skills.filter((skill) => skill.enabled !== false).map((skill) => skill.name),
|
|
523
523
|
contextFiles: sessionContext.contextFiles.filter((contextFile) => contextFile.enabled !== false).map(contextFileKey),
|
|
524
|
+
diagnostics: sessionContext.diagnostics.map((diagnostic) => ({ ...diagnostic })),
|
|
524
525
|
subagents: sessionContext.subagents.filter((subagent) => subagent.enabled !== false),
|
|
525
526
|
mcpServers: [...sessionContext.mcpServers],
|
|
526
527
|
piPackages: sessionContext.piPackages.filter((pkg) => pkg.enabled !== false).map((pkg) => pkg.id),
|
|
527
528
|
model: sessionContext.model ? { ...sessionContext.model } : undefined,
|
|
528
529
|
mainModel: sessionContext.mainModel ? { ...sessionContext.mainModel } : undefined,
|
|
530
|
+
mainModelFallbacks: sessionContext.mainModelFallbacks.map((model) => ({ ...model })),
|
|
529
531
|
subagentModel: sessionContext.subagentModel ? { ...sessionContext.subagentModel } : undefined,
|
|
530
532
|
thinkingLevel: sessionContext.thinkingLevel,
|
|
531
533
|
mainThinkingLevel: sessionContext.mainThinkingLevel,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createPiboChatCustomAgentProfilesPlugin, } from "./chat-custom-agents.js";
|
|
2
|
+
import { createPiboChatUserSkillsPlugin } from "./chat-user-skills.js";
|
|
3
|
+
import { createPiboContextFilesCatalogPlugin, createPiboContextFilesPlugin, } from "./context-files.js";
|
|
4
|
+
import { createDefaultPiboPluginRegistry } from "./builtin.js";
|
|
5
|
+
export function createPiboUserProfileResourcePlugins(options = {}) {
|
|
6
|
+
return [
|
|
7
|
+
createPiboChatUserSkillsPlugin(options.userSkills),
|
|
8
|
+
options.contextFilesMode === "catalog"
|
|
9
|
+
? createPiboContextFilesCatalogPlugin(options.contextFiles)
|
|
10
|
+
: createPiboContextFilesPlugin(options.contextFiles),
|
|
11
|
+
createPiboChatCustomAgentProfilesPlugin(options.customAgents),
|
|
12
|
+
];
|
|
13
|
+
}
|
|
14
|
+
export function createDefaultPiboUserProfileRegistry(options = {}) {
|
|
15
|
+
const registry = createDefaultPiboPluginRegistry();
|
|
16
|
+
registerPiboUserProfileResources(registry, { contextFilesMode: "catalog", ...options });
|
|
17
|
+
return registry;
|
|
18
|
+
}
|
|
19
|
+
export function registerPiboUserProfileResources(registry, options = {}) {
|
|
20
|
+
for (const plugin of createPiboUserProfileResourcePlugins(options))
|
|
21
|
+
registry.registerPlugin(plugin);
|
|
22
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { isIP } from "node:net";
|
|
2
|
+
function isLocalPreviewHostname(hostname) {
|
|
3
|
+
return hostname === "localhost" || hostname.endsWith(".localhost");
|
|
4
|
+
}
|
|
5
|
+
function isValidDnsHostname(hostname) {
|
|
6
|
+
return isIP(hostname) === 0 &&
|
|
7
|
+
hostname.length <= 253 &&
|
|
8
|
+
!hostname.endsWith(".") &&
|
|
9
|
+
hostname.split(".").every((label) => label.length >= 1 &&
|
|
10
|
+
label.length <= 63 &&
|
|
11
|
+
/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i.test(label));
|
|
12
|
+
}
|
|
13
|
+
export function parsePreviewBaseURL(value) {
|
|
14
|
+
let url;
|
|
15
|
+
try {
|
|
16
|
+
url = new URL(value);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
throw new Error("preview.baseURL must be an absolute HTTP or HTTPS URL");
|
|
20
|
+
}
|
|
21
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
22
|
+
throw new Error("preview.baseURL must use http or https");
|
|
23
|
+
}
|
|
24
|
+
if (url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
25
|
+
throw new Error("preview.baseURL must contain only scheme, hostname, and optional port");
|
|
26
|
+
}
|
|
27
|
+
if (!isValidDnsHostname(url.hostname)) {
|
|
28
|
+
throw new Error("preview.baseURL hostname must be a DNS hostname without wildcards, IP literals, or a trailing dot");
|
|
29
|
+
}
|
|
30
|
+
if (url.protocol === "http:" && !isLocalPreviewHostname(url.hostname)) {
|
|
31
|
+
throw new Error("preview.baseURL must use https except for localhost development");
|
|
32
|
+
}
|
|
33
|
+
return url;
|
|
34
|
+
}
|