diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b4b123..b14c390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## Unreleased +### Added + +- **OMP status segment** — publishes `Telegram: conectado` or `Telegram: desconectado` immediately after OMP's `cost` segment through the public `setStatusSegment()` API. + +### Changed + +- **OMP compatibility** — require `@oh-my-pi/pi-coding-agent >=17.0.0`; README installation commands now use `omp plugin install` and `omp plugin link`. + ### 0.22.0-evandro.1 — Evandro Fonseca Junior fork (oh-my-pi-telegram) - **Repackaged as `@evandrodevbr/oh-my-pi-telegram`** — version suffix `-evandro.1` keeps the 0.22.0 source line (SHA `afe09c5`) traceable to upstream `llblab/pi-telegram` while making the fork unambiguous in the `omp` extension registry. diff --git a/README.md b/README.md index 18e3793..a528d4e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ This fork is built and tested against: | Component | Version | Status | | --- | --- | --- | -| `can1357/oh-my-pi` (CLI: `omp`) | `>=16.5.2` (`@oh-my-pi/pi-coding-agent`) | ✅ verified | +| `can1357/oh-my-pi` (CLI: `omp`) | `>=17.0.0` (`@oh-my-pi/pi-coding-agent`) | ✅ verified | | `@earendil-works/pi-{ai,agent-core,coding-agent}` | `>=0.80.6` | ✅ verified (peerDeps preserved from upstream) | | `node` | `>=22.19.0` | ✅ verified on Node 24.15.0 | | Telegram Bot API | current | ✅ unchanged from upstream | @@ -23,13 +23,13 @@ This fork is built and tested against: ```bash # From npm (after this fork is published): -omp install npm:@evandrodevbr/oh-my-pi-telegram +omp plugin install npm:@evandrodevbr/oh-my-pi-telegram # From git (always works): -omp install git:github.com/evandrodevbr/oh-my-pi-telegram +omp plugin install git:github.com/evandrodevbr/oh-my-pi-telegram # From a local checkout (development): -omp install --link /path/to/oh-my-pi-telegram +omp plugin link /path/to/oh-my-pi-telegram ``` Then inside `omp`: diff --git a/index.ts b/index.ts index cfc710e..151c78c 100644 --- a/index.ts +++ b/index.ts @@ -278,6 +278,7 @@ export default function (pi: Pi.ExtensionAPI) { Queue.TelegramQueueItem >({ getConfig: configStore.get, + setStatusSegment: Pi.setExtensionContextTopStatusSegment, getActiveProfileName: configStore.getActiveProfileName, getDiagnosticPaths: Paths.getTelegramDiagnosticsDisplayPaths, isPollingActive: Polling.createTelegramPollingActivityReader( diff --git a/lib/pi.ts b/lib/pi.ts index 871dd73..e2a383d 100644 --- a/lib/pi.ts +++ b/lib/pi.ts @@ -95,6 +95,23 @@ export function canStartPollingInExtensionContext(ctx: unknown): boolean { return !isExtensionContextPassiveRunMode(ctx); } +export interface PiTopStatusSegmentPorts { + setStatusSegment?: ( + key: string, + text: string | undefined, + options: { placement: "after-cost" }, + ) => void; +} + +export function setExtensionContextTopStatusSegment( + ctx: ExtensionContext, + key: string, + text: string, +): void { + const ui = ctx.ui as ExtensionContext["ui"] & PiTopStatusSegmentPorts; + ui.setStatusSegment?.(key, text, { placement: "after-cost" }); +} + export function formatPollingStartBlockedByRunMode(ctx: unknown): string { const mode = getExtensionContextMode(ctx); return mode diff --git a/lib/status.ts b/lib/status.ts index 9c3321a..736efba 100644 --- a/lib/status.ts +++ b/lib/status.ts @@ -232,6 +232,14 @@ export interface TelegramStatusBarState { error?: string; } +export function getTelegramTopStatusSegment( + state: Pick, +): string { + return state.pollingActive + ? "Telegram: conectado" + : "Telegram: desconectado"; +} + export interface TelegramStatusRuntimeContext { ui: { theme: TelegramStatusBarTheme; @@ -245,6 +253,7 @@ export interface TelegramStatusRuntimeDeps< statusKey?: string; getStatusBarState: (ctx: TContext, error?: string) => TelegramStatusBarState; getBridgeStatusLineState: () => TelegramBridgeStatusLineState; + setStatusSegment?: (ctx: TContext, key: string, text: string) => void; } export interface TelegramBridgeStatusConfig { @@ -255,6 +264,7 @@ export interface TelegramBridgeStatusConfig { } export interface TelegramBridgeStatusRuntimeDeps< + TContext extends TelegramStatusRuntimeContext, TQueueItem extends { queueLane: TelegramStatusQueueLane }, > { statusKey?: string; @@ -298,6 +308,11 @@ export interface TelegramBridgeStatusRuntimeDeps< getInstanceSlot?: () => string | undefined; getInstanceThreadName?: () => string | undefined; getNowMs?: () => number; + setStatusSegment?: ( + ctx: TContext, + key: string, + text: string, + ) => void; } export interface TelegramBridgeStatusLineOptions { @@ -565,12 +580,15 @@ export function createTelegramStatusRuntime< const statusKey = deps.statusKey ?? "telegram"; return { updateStatus: (ctx, error) => { + const state = deps.getStatusBarState(ctx, error); ctx.ui.setStatus( statusKey, - buildTelegramStatusBarText( - ctx.ui.theme, - deps.getStatusBarState(ctx, error), - ), + buildTelegramStatusBarText(ctx.ui.theme, state), + ); + deps.setStatusSegment?.( + ctx, + statusKey, + getTelegramTopStatusSegment(state), ); }, getStatusLines: (options) => @@ -583,10 +601,11 @@ export function createTelegramBridgeStatusRuntime< TContext extends TelegramStatusRuntimeContext, TQueueItem extends { queueLane: TelegramStatusQueueLane }, >( - deps: TelegramBridgeStatusRuntimeDeps, + deps: TelegramBridgeStatusRuntimeDeps, ): TelegramStatusRuntime { return createTelegramStatusRuntime({ statusKey: deps.statusKey, + setStatusSegment: deps.setStatusSegment, getStatusBarState: (_ctx, error) => { const config = deps.getConfig(); const queuedItems = deps.getQueuedItems(); diff --git a/package.json b/package.json index d79e22b..b11e9c1 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "./index.ts" ], "compatibleWith": { - "@oh-my-pi/pi-coding-agent": ">=16.5.2" + "@oh-my-pi/pi-coding-agent": ">=17.0.0" } }, "peerDependencies": { diff --git a/tests/public-api.test.ts b/tests/public-api.test.ts index 3b765e1..d055fd7 100644 --- a/tests/public-api.test.ts +++ b/tests/public-api.test.ts @@ -100,7 +100,7 @@ test("Package declares the OMP extension entry point and Pi lifecycle compatibil assert.deepEqual(packageJson.omp?.extensions, ["./index.ts"]); assert.equal( packageJson.omp?.compatibleWith?.["@oh-my-pi/pi-coding-agent"], - ">=16.5.2", + ">=17.0.0", ); assert.equal( packageJson.peerDependencies?.["@earendil-works/pi-coding-agent"], diff --git a/tests/status.test.ts b/tests/status.test.ts index 301302d..2c3bad1 100644 --- a/tests/status.test.ts +++ b/tests/status.test.ts @@ -18,6 +18,7 @@ import { createTelegramStatusHtmlBuilder, createTelegramStatusSnapshot, createTelegramStatusRuntime, + getTelegramTopStatusSegment, getTelegramStatusBarProcessingStatus, recordStructuredTelegramRuntimeEvent, recordTelegramRuntimeEvent, @@ -401,6 +402,56 @@ test("Status runtime updates the status bar and exposes bridge lines", () => { ]); }); +test("Status runtime publishes the polling state after cost", () => { + const published: Array<[string, string, { placement: "after-cost" }]> = []; + let pollingActive = true; + const runtime = createTelegramBridgeStatusRuntime({ + getConfig: () => ({ botToken: "token", allowedUserId: 7 }), + isPollingActive: () => pollingActive, + getActiveSourceMessageIds: () => undefined, + hasActiveTurn: () => false, + hasDispatchPending: () => false, + isCompactionInProgress: () => false, + getActiveToolExecutions: () => 0, + hasPendingModelSwitch: () => false, + getQueuedItems: () => [], + formatQueuedStatus: () => "", + getRecentRuntimeEvents: () => [], + setStatusSegment: (_ctx, key, text) => { + published.push([key, text, { placement: "after-cost" }]); + }, + }); + const ctx = { + ui: { + theme: { fg: (_token: string, text: string) => text }, + setStatus: () => {}, + }, + }; + + assert.equal( + getTelegramTopStatusSegment({ pollingActive: true }), + "Telegram: conectado", + ); + runtime.updateStatus(ctx); + assert.deepEqual(published.pop(), [ + "telegram", + "Telegram: conectado", + { placement: "after-cost" }, + ]); + + pollingActive = false; + assert.equal( + getTelegramTopStatusSegment({ pollingActive: false }), + "Telegram: desconectado", + ); + runtime.updateStatus(ctx); + assert.deepEqual(published.pop(), [ + "telegram", + "Telegram: desconectado", + { placement: "after-cost" }, + ]); +}); + test("Status lines expose thread reconciliation state", () => { const lines = buildTelegramBridgeStatusLines({ botUsername: "demo_bot",