jeopi-cli 16.2.26 → 16.2.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.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/dist/cli.js +2574 -2581
- package/dist/types/commands/install.d.ts +1 -1
- package/dist/types/discovery/helpers.d.ts +1 -1
- package/dist/types/export/html/index.d.ts +2 -2
- package/dist/types/export/html/web-palette.d.ts +2 -2
- package/dist/types/tiny/text.d.ts +9 -2
- package/dist/types/tools/report-tool-issue.d.ts +1 -1
- package/package.json +12 -12
- package/scripts/generate-share-viewer.ts +5 -5
- package/scripts/jeopi +4 -4
- package/scripts/{omp.ts → jeopi-preload.ts} +4 -4
- package/src/commands/install.ts +1 -1
- package/src/debug/report-bundle.ts +1 -1
- package/src/debug/system-info.ts +2 -2
- package/src/discovery/builtin.ts +4 -4
- package/src/discovery/helpers.ts +2 -2
- package/src/discovery/omp-plugins.ts +3 -3
- package/src/discovery/ssh.ts +1 -1
- package/src/export/html/index.ts +2 -2
- package/src/export/html/share-loader.js +5 -5
- package/src/export/html/template.css +2 -2
- package/src/export/html/template.js +5 -5
- package/src/export/html/tool-views.generated.js +2 -2
- package/src/export/html/web-palette.ts +2 -2
- package/src/export/share.ts +1 -1
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/memories/index.ts +23 -1
- package/src/modes/acp/acp-agent.ts +12 -6
- package/src/modes/controllers/selector-controller.ts +16 -2
- package/src/session/agent-session.ts +13 -15
- package/src/tiny/text.ts +11 -8
- package/src/tools/image-gen.ts +2 -4
- package/src/tools/report-tool-issue.ts +1 -1
package/src/memories/index.ts
CHANGED
|
@@ -698,12 +698,34 @@ function extractPersistableMessages(payload: string): AgentMessage[] {
|
|
|
698
698
|
if (!maybeMessage || typeof maybeMessage !== "object") continue;
|
|
699
699
|
const message = maybeMessage as AgentMessage;
|
|
700
700
|
if (shouldPersistResponseItemForMemories(message)) {
|
|
701
|
-
messages.push(message);
|
|
701
|
+
messages.push(dropThinkingContentForMemories(message));
|
|
702
702
|
}
|
|
703
703
|
}
|
|
704
704
|
return messages;
|
|
705
705
|
}
|
|
706
706
|
|
|
707
|
+
/**
|
|
708
|
+
* Drop `thinking`/`redactedThinking` content blocks from an assistant message
|
|
709
|
+
* before it is JSON-serialized into the stage-one extraction prompt. The raw
|
|
710
|
+
* message object (not just its extracted text) is embedded verbatim as
|
|
711
|
+
* `response_items_json` in `stage_one_input.md`, so any reasoning block left
|
|
712
|
+
* in `content` would be replayed as plaintext JSON back to the model — a
|
|
713
|
+
* documented Anthropic `reasoning_extraction` refusal trigger. Dropped, not
|
|
714
|
+
* reformatted (mirrors `hindsight/transcript.ts` and `compaction/utils.ts`).
|
|
715
|
+
*/
|
|
716
|
+
function dropThinkingContentForMemories(message: AgentMessage): AgentMessage {
|
|
717
|
+
const role = (message as { role?: string }).role;
|
|
718
|
+
if (role !== "assistant") return message;
|
|
719
|
+
const content = (message as { content?: unknown }).content;
|
|
720
|
+
if (!Array.isArray(content)) return message;
|
|
721
|
+
const filtered = content.filter(block => {
|
|
722
|
+
const type = (block as { type?: string }).type;
|
|
723
|
+
return type !== "thinking" && type !== "redactedThinking";
|
|
724
|
+
});
|
|
725
|
+
if (filtered.length === content.length) return message;
|
|
726
|
+
return { ...message, content: filtered } as AgentMessage;
|
|
727
|
+
}
|
|
728
|
+
|
|
707
729
|
async function runStage1Job(options: {
|
|
708
730
|
claim: Stage1Claim;
|
|
709
731
|
model: Model;
|
|
@@ -177,6 +177,8 @@ type ReplayableMessage = {
|
|
|
177
177
|
toolName?: string;
|
|
178
178
|
details?: unknown;
|
|
179
179
|
isError?: boolean;
|
|
180
|
+
/** `custom`/`hookMessage` visibility flag; absent (e.g. `user`/`developer`) means no gate. */
|
|
181
|
+
display?: boolean;
|
|
180
182
|
};
|
|
181
183
|
|
|
182
184
|
type ReplayableToolItem = {
|
|
@@ -1906,12 +1908,16 @@ export class AcpAgent implements Agent {
|
|
|
1906
1908
|
if (message.role === "assistant") {
|
|
1907
1909
|
return this.#replayAssistantMessage(sessionId, message, cwd, replayedToolCallIds, replayedToolCallArgs);
|
|
1908
1910
|
}
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
)
|
|
1911
|
+
// `developer` carries jeopi-internal reminders/nudges (e.g. `<system-reminder>`
|
|
1912
|
+
// todo nudges) — the TUI never renders these (blanked to empty text in both the
|
|
1913
|
+
// live and history-rebuild paths; see chat-transcript-builder.ts / ui-helpers.ts),
|
|
1914
|
+
// so ACP replay must not surface them to the client either. `custom`/`hookMessage`
|
|
1915
|
+
// carry the same `display` gate the TUI checks (`#appendCustomMessage` /
|
|
1916
|
+
// ui-helpers.ts's `if (message.display)`) — a hidden one (e.g. the resolve-preview
|
|
1917
|
+
// reminder, the todo-error reminder) must stay hidden on replay too.
|
|
1918
|
+
const replayableAsUserChunk =
|
|
1919
|
+
message.role === "user" || ((message.role === "custom" || message.role === "hookMessage") && message.display);
|
|
1920
|
+
if (replayableAsUserChunk) {
|
|
1915
1921
|
return this.#wrapReplayContent(
|
|
1916
1922
|
sessionId,
|
|
1917
1923
|
this.#extractReplayContent(message.content, undefined),
|
|
@@ -4,7 +4,7 @@ import { getOAuthProviders } from "jeopi-ai/oauth";
|
|
|
4
4
|
import type { OAuthProvider } from "jeopi-ai/oauth/types";
|
|
5
5
|
import type { Component, OverlayHandle } from "jeopi-tui";
|
|
6
6
|
import { Input, Loader, Spacer, setTuiTight, Text } from "jeopi-tui";
|
|
7
|
-
import { getAgentDbPath, getAgentDir, getProjectDir, normalizePathForComparison } from "jeopi-utils";
|
|
7
|
+
import { formatNumber, getAgentDbPath, getAgentDir, getProjectDir, normalizePathForComparison } from "jeopi-utils";
|
|
8
8
|
import {
|
|
9
9
|
type AdvisorConfigScope,
|
|
10
10
|
discoverAdvisorConfigs,
|
|
@@ -633,8 +633,22 @@ export class SelectorController {
|
|
|
633
633
|
if (switched) {
|
|
634
634
|
this.ctx.statusLine.invalidate();
|
|
635
635
|
this.ctx.updateEditorBorderColor();
|
|
636
|
+
this.ctx.showStatus(`Default model: ${selector ?? model.id}`);
|
|
637
|
+
} else {
|
|
638
|
+
// `setModel` deliberately skips the live switch (while still
|
|
639
|
+
// persisting the role) when the session's current context token
|
|
640
|
+
// count already exceeds the candidate's context window — sending
|
|
641
|
+
// on that model would just fail with a context-overflow error.
|
|
642
|
+
// This is exactly the moment a user hits a usage/token limit and
|
|
643
|
+
// reaches for /model to escape it, so the picker must say the
|
|
644
|
+
// active model did NOT change instead of the generic success
|
|
645
|
+
// status — otherwise the switch looks silently ignored.
|
|
646
|
+
this.ctx.showError(
|
|
647
|
+
`Default model set to ${selector ?? model.id} for future sessions, but NOT applied now: ` +
|
|
648
|
+
`current context (${formatNumber(currentContextTokens)} tokens) exceeds its ` +
|
|
649
|
+
`${formatNumber(model.contextWindow ?? 0)}-token window. Compact (/compact) or start a new session to switch now.`,
|
|
650
|
+
);
|
|
636
651
|
}
|
|
637
|
-
this.ctx.showStatus(`Default model: ${selector ?? model.id}`);
|
|
638
652
|
// Don't call done() - selector stays open for role assignment
|
|
639
653
|
} else {
|
|
640
654
|
// Other roles (smol, slow): just update settings, not current model
|
|
@@ -1472,17 +1472,6 @@ function textFromContent(content: unknown): string {
|
|
|
1472
1472
|
return parts.join("\n\n");
|
|
1473
1473
|
}
|
|
1474
1474
|
|
|
1475
|
-
function thinkingFromContent(content: unknown): string {
|
|
1476
|
-
if (!Array.isArray(content)) return "";
|
|
1477
|
-
const parts: string[] = [];
|
|
1478
|
-
for (const block of content) {
|
|
1479
|
-
if (!isRecord(block) || block.type !== "thinking" || typeof block.thinking !== "string") continue;
|
|
1480
|
-
const thinking = block.thinking.trim();
|
|
1481
|
-
if (thinking) parts.push(thinking);
|
|
1482
|
-
}
|
|
1483
|
-
return parts.join("\n\n");
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
1475
|
function toolCallOpFromMessage(message: AgentMessage, toolCallId: string): string | undefined {
|
|
1487
1476
|
if (message.role !== "assistant" || !Array.isArray(message.content)) return undefined;
|
|
1488
1477
|
for (const block of message.content) {
|
|
@@ -1492,12 +1481,21 @@ function toolCallOpFromMessage(message: AgentMessage, toolCallId: string): strin
|
|
|
1492
1481
|
return undefined;
|
|
1493
1482
|
}
|
|
1494
1483
|
|
|
1484
|
+
/**
|
|
1485
|
+
* Title context turns carry `text` only — no `thinking`. The replan-refresh
|
|
1486
|
+
* title model resolves through the `tiny`/`commit`/`smol` roles but falls back
|
|
1487
|
+
* to the session's own current model (`getTitleModel` in title-generator.ts)
|
|
1488
|
+
* when no such role is configured, which can be the same Anthropic model the
|
|
1489
|
+
* primary turn used. Replaying prior assistant thinking as plaintext back to
|
|
1490
|
+
* that model is a `reasoning_extraction` classifier-refusal trigger (see the
|
|
1491
|
+
* intentional thinking-drop in compaction/utils.ts, memories/index.ts, and the
|
|
1492
|
+
* hindsight/mnemopi transcript extractor) — dropped here for the same reason.
|
|
1493
|
+
*/
|
|
1495
1494
|
function titleConversationTurnFromMessage(message: AgentMessage): TitleConversationTurn | undefined {
|
|
1496
1495
|
if (message.role !== "user" && message.role !== "assistant") return undefined;
|
|
1497
1496
|
const text = textFromContent(message.content);
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
return { role: message.role, ...(text ? { text } : {}), ...(thinking ? { thinking } : {}) };
|
|
1497
|
+
if (!text) return undefined;
|
|
1498
|
+
return { role: message.role, text };
|
|
1501
1499
|
}
|
|
1502
1500
|
|
|
1503
1501
|
export class AgentSession {
|
|
@@ -15195,7 +15193,7 @@ export class AgentSession {
|
|
|
15195
15193
|
* @returns Path to exported file
|
|
15196
15194
|
*/
|
|
15197
15195
|
async exportToHtml(outputPath?: string): Promise<string> {
|
|
15198
|
-
// Public HTML export ships in the
|
|
15196
|
+
// Public HTML export ships in the jeopi brand palette (collab-web
|
|
15199
15197
|
// pink/purple), matching my.omp.sh — not the host's terminal theme.
|
|
15200
15198
|
// Callers who want a themed export can pass `palette: "theme"` with
|
|
15201
15199
|
// `themeName` directly to `exportSessionToHtml`.
|
package/src/tiny/text.ts
CHANGED
|
@@ -47,20 +47,23 @@ export function formatTitleUserMessage(message: string): string {
|
|
|
47
47
|
export interface TitleConversationTurn {
|
|
48
48
|
role: "user" | "assistant";
|
|
49
49
|
text?: string;
|
|
50
|
-
thinking?: string;
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Format recent user/assistant context for title generation after a todo
|
|
54
|
+
* replan. Assistant `thinking` is intentionally never included: the
|
|
55
|
+
* replan-refresh title model falls back to the session's own current model
|
|
56
|
+
* when no `tiny`/`commit`/`smol` role is configured, so replaying prior
|
|
57
|
+
* reasoning as plaintext here is a `reasoning_extraction` classifier-refusal
|
|
58
|
+
* trigger on Anthropic targets (mirrors the thinking-drop in compaction,
|
|
59
|
+
* memories, and the hindsight transcript extractor).
|
|
60
|
+
*/
|
|
54
61
|
export function formatTitleConversationContext(turns: readonly TitleConversationTurn[]): string {
|
|
55
62
|
const formattedTurns: string[] = [];
|
|
56
63
|
for (const turn of turns) {
|
|
57
|
-
const sections: string[] = [];
|
|
58
64
|
const text = turn.text?.trim();
|
|
59
|
-
if (text)
|
|
60
|
-
|
|
61
|
-
if (thinking) sections.push(`<thinking>\n${thinking}\n</thinking>`);
|
|
62
|
-
if (sections.length === 0) continue;
|
|
63
|
-
formattedTurns.push(`<${turn.role}>\n${sections.join("\n\n")}\n</${turn.role}>`);
|
|
65
|
+
if (!text) continue;
|
|
66
|
+
formattedTurns.push(`<${turn.role}>\n${text}\n</${turn.role}>`);
|
|
64
67
|
}
|
|
65
68
|
if (formattedTurns.length === 0) return "";
|
|
66
69
|
return prepareTitleInput(`<conversation>\n${formattedTurns.join("\n\n")}\n</conversation>`);
|
package/src/tools/image-gen.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as os from "node:os";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { type } from "arktype";
|
|
4
|
-
import { type ApiKey, type FetchImpl, getEnvApiKey, type Model, withAuth } from "jeopi-ai";
|
|
4
|
+
import { type ApiKey, type FetchImpl, getEnvApiKey, getOpenRouterHeaders, type Model, withAuth } from "jeopi-ai";
|
|
5
5
|
import { ProviderHttpError } from "jeopi-ai/error";
|
|
6
6
|
import {
|
|
7
7
|
CODEX_BASE_URL,
|
|
@@ -1394,9 +1394,7 @@ export const imageGenTool: CustomTool<typeof imageGenSchema, ImageGenToolDetails
|
|
|
1394
1394
|
headers: {
|
|
1395
1395
|
"Content-Type": "application/json",
|
|
1396
1396
|
Authorization: `Bearer ${key}`,
|
|
1397
|
-
|
|
1398
|
-
"X-OpenRouter-Title": "Oh-My-Pi",
|
|
1399
|
-
"X-OpenRouter-Categories": "cli-agent",
|
|
1397
|
+
...getOpenRouterHeaders(),
|
|
1400
1398
|
},
|
|
1401
1399
|
body: JSON.stringify(requestBody),
|
|
1402
1400
|
signal: requestSignal,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* (including from subagents) read the cached decision without prompting.
|
|
14
14
|
*
|
|
15
15
|
* When the user grants consent, push is automatically active against the
|
|
16
|
-
* bundled endpoint (`dev.autoqaPush.endpoint`, default `qa.
|
|
16
|
+
* bundled endpoint (`dev.autoqaPush.endpoint`, default `qa.jeopi.sh`). Each
|
|
17
17
|
* insert schedules a background flush that POSTs pending rows and deletes
|
|
18
18
|
* them on HTTP 2xx. `PI_AUTO_QA_PUSH=1` forces push in non-interactive
|
|
19
19
|
* environments where the consent dialog never fires. Tool execution is
|