jeopi-cli 16.6.8 → 16.6.11

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.
@@ -0,0 +1,28 @@
1
+ import type { InteractiveModeContext } from "../modes/types";
2
+ import { createDiscordAdapter, type DiscordTransportDeps } from "./discord";
3
+ import { createMessengerGateway, type MessengerGateway } from "./gateway";
4
+ import type { MessengerConfig } from "./types";
5
+ export interface LiveDiscordBridgeDependencies {
6
+ createAdapter?: typeof createDiscordAdapter;
7
+ createGateway?: typeof createMessengerGateway;
8
+ env?: Record<string, string | undefined>;
9
+ transport?: DiscordTransportDeps;
10
+ pollIntervalMs?: number;
11
+ }
12
+ export interface LiveDiscordBridge {
13
+ readonly gateway: MessengerGateway;
14
+ readonly active: boolean;
15
+ run(signal?: AbortSignal): Promise<void>;
16
+ close(): Promise<void>;
17
+ }
18
+ export interface LiveDiscordBridgeOptions {
19
+ /** Explicit acknowledgement that Discord requests may use this host session's tools. */
20
+ acknowledgeFullControl?: boolean;
21
+ }
22
+ /**
23
+ * Attach an authorized Discord transport to the already-running interactive session.
24
+ * This never creates, switches, or disposes an AgentSession. The caller owns activation
25
+ * and must retain the returned bridge to close it when the TUI shuts down.
26
+ */
27
+ export declare function createLiveDiscordBridge(ctx: InteractiveModeContext, config: MessengerConfig, dependencies?: LiveDiscordBridgeDependencies, options?: LiveDiscordBridgeOptions): LiveDiscordBridge;
28
+ export declare function loadAndCreateLiveDiscordBridge(ctx: InteractiveModeContext, configPath: string, dependencies?: LiveDiscordBridgeDependencies, options?: LiveDiscordBridgeOptions): Promise<LiveDiscordBridge>;
@@ -11,6 +11,7 @@ import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectIte
11
11
  import type { CompactOptions } from "../extensibility/extensions/types";
12
12
  import type { Skill } from "../extensibility/skills";
13
13
  import type { MCPManager } from "../mcp";
14
+ import type { LiveDiscordBridge } from "../messenger/live-session";
14
15
  import { type PlanApprovalDetails } from "../plan-mode/approved-plan";
15
16
  import { type AgentSession } from "../session/agent-session";
16
17
  import type { CompactMode } from "../session/compact-modes";
@@ -157,6 +158,7 @@ export declare class InteractiveMode implements InteractiveModeContext {
157
158
  oauthManualInput: OAuthManualInputManager;
158
159
  collabHost?: CollabHost;
159
160
  collabGuest?: CollabGuestLink;
161
+ liveDiscordBridge?: LiveDiscordBridge;
160
162
  readonly lspServers: LspStartupServerInfo[] | undefined;
161
163
  mcpManager?: MCPManager;
162
164
  get eventController(): EventController;
@@ -15,11 +15,16 @@ export interface SessionTeardownDeps {
15
15
  /** Snapshot the current editor text; called once, before disposal touches session state. */
16
16
  getDraftText: () => string;
17
17
  /**
18
- * Synchronously mark the session as disposing before any awaited teardown
19
- * work. This closes the async gap where deferred jobs could otherwise start
20
- * after a signal requested shutdown but before `disposeSession()` begins.
18
+ * Synchronously mark the session as disposing after asynchronous revocation
19
+ * completes and before any further awaited teardown work. This closes the async
20
+ * gap where deferred jobs could otherwise start after a signal requested shutdown.
21
21
  */
22
22
  beginDispose: () => void;
23
+ /**
24
+ * Complete asynchronous revocation before the session is marked disposed.
25
+ * Failures are logged and never skip the disposal chain.
26
+ */
27
+ beforeDispose?: () => Promise<void>;
23
28
  /**
24
29
  * Persist the snapshotted draft. Called even for an empty string so a
25
30
  * previously-persisted draft sidecar is cleared on a clean exit.
@@ -41,11 +46,11 @@ export interface SessionTeardownDeps {
41
46
  */
42
47
  export type SessionTeardown = (reason?: postmortem.Reason) => Promise<void>;
43
48
  /**
44
- * Build a promise-memoized teardown function. The first call snapshots the
45
- * draft text, marks the session disposing synchronously, runs `saveDraft`
46
- * (draft-loss protection for `--resume`), then `disposeSession`; subsequent
47
- * calls await the same settled promise, so the keypress
48
- * `InteractiveMode.shutdown()` path and the postmortem signal callback cannot
49
+ * Build a promise-memoized teardown: snapshot the draft, complete asynchronous
50
+ * revocation, mark the session disposing synchronously, run `saveDraft` (draft-loss
51
+ * protection for `--resume`), then `disposeSession`; subsequent calls await the same
52
+ * settled promise so the keypress `InteractiveMode.shutdown()` path and the postmortem
53
+ * signal callback cannot
49
54
  * double-emit `session_shutdown`, double-dispose the session's async-job
50
55
  * manager, or race each other.
51
56
  *
@@ -10,6 +10,7 @@ import type { ExtensionUIContext, ExtensionUIDialogOptions, ExtensionUISelectIte
10
10
  import type { CompactOptions } from "../extensibility/extensions/types";
11
11
  import type { Skill } from "../extensibility/skills";
12
12
  import type { MCPManager } from "../mcp";
13
+ import type { LiveDiscordBridge } from "../messenger/live-session";
13
14
  import type { PlanApprovalDetails } from "../plan-mode/approved-plan";
14
15
  import type { AgentSession } from "../session/agent-session";
15
16
  import type { CompactMode } from "../session/compact-modes";
@@ -117,6 +118,7 @@ export interface InteractiveModeContext {
117
118
  collabGuest?: CollabGuestLink;
118
119
  eventController: EventController;
119
120
  eventBus?: EventBus;
121
+ liveDiscordBridge?: LiveDiscordBridge;
120
122
  isInitialized: boolean;
121
123
  /**
122
124
  * `true` once `renderInitialMessages` has rendered the session transcript
@@ -5,7 +5,7 @@
5
5
  * providers with provider-specific parameters exposed conditionally.
6
6
  */
7
7
  import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "jeopi-agent-core";
8
- import type { AuthStorage } from "jeopi-ai";
8
+ import type { Api, AuthStorage, Model } from "jeopi-ai";
9
9
  import type { CustomTool } from "../../extensibility/custom-tools/types";
10
10
  import type { ToolSession } from "../../tools";
11
11
  import { type SearchRenderDetails } from "./render";
@@ -32,6 +32,7 @@ export interface SearchQueryParams extends SearchToolParams {
32
32
  */
33
33
  export declare function runSearchQuery(params: SearchQueryParams, options?: {
34
34
  authStorage?: AuthStorage;
35
+ model?: Model<Api>;
35
36
  sessionId?: string;
36
37
  signal?: AbortSignal;
37
38
  }): Promise<{
@@ -1,5 +1,8 @@
1
- import type { AuthStorage, FetchImpl } from "jeopi-ai";
1
+ import type { Api, AuthStorage, FetchImpl, Model } from "jeopi-ai";
2
2
  import type { SearchProviderId, SearchResponse } from "../types";
3
+ export type GeminiOAuthProvider = "google-gemini-cli" | "google-antigravity";
4
+ /** Return the OAuth provider owned by a matching Gemini model, if any. */
5
+ export declare function resolveGeminiOAuthProvider(model?: Model<Api>): GeminiOAuthProvider | undefined;
3
6
  /**
4
7
  * Shared web search parameters passed to providers.
5
8
  *
@@ -50,6 +53,8 @@ export interface SearchParams {
50
53
  * caller's agent session when available; otherwise omit.
51
54
  */
52
55
  sessionId?: string;
56
+ /** Active session model, when the caller has one. Providers may use it only when it matches their protocol. */
57
+ model?: Model<Api>;
53
58
  antigravityEndpointMode?: "auto" | "production" | "sandbox";
54
59
  }
55
60
  /** Base class for web search providers. */
@@ -8,9 +8,9 @@
8
8
  * sibling SQLite store and never POSTs the broker sentinel to a Google token
9
9
  * endpoint.
10
10
  */
11
- import { type AuthStorage, type FetchImpl, type OAuthAccess } from "jeopi-ai";
11
+ import { type Api, type AuthStorage, type FetchImpl, type Model, type OAuthAccess } from "jeopi-ai";
12
12
  import type { SearchResponse } from "../../../web/search/types";
13
- import type { SearchParams } from "./base";
13
+ import type { GeminiOAuthProvider, SearchParams } from "./base";
14
14
  import { SearchProvider } from "./base";
15
15
  declare const GEMINI_PROVIDERS: readonly ["google-gemini-cli", "google-antigravity"];
16
16
  type GeminiProviderId = (typeof GEMINI_PROVIDERS)[number];
@@ -29,6 +29,7 @@ export interface GeminiSearchParams extends GeminiToolParams {
29
29
  temperature?: number;
30
30
  signal?: AbortSignal;
31
31
  authStorage: AuthStorage;
32
+ model?: Model<Api>;
32
33
  sessionId?: string;
33
34
  fetch?: FetchImpl;
34
35
  antigravityEndpointMode?: "auto" | "production" | "sandbox";
@@ -47,7 +48,7 @@ interface GeminiAuthSeed {
47
48
  * routing internally; this helper never touches refresh tokens directly.
48
49
  * The resolved access seeds `withOAuthAccess` so the happy path resolves once.
49
50
  */
50
- export declare function findGeminiAuth(authStorage: AuthStorage, sessionId: string | undefined, signal: AbortSignal | undefined): Promise<GeminiAuthSeed | null>;
51
+ export declare function findGeminiAuth(authStorage: AuthStorage, sessionId: string | undefined, signal: AbortSignal | undefined, preferredProvider?: GeminiOAuthProvider): Promise<GeminiAuthSeed | null>;
51
52
  /**
52
53
  * Executes a web search using Google Gemini with Google Search grounding.
53
54
  */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "jeopi-cli",
4
- "version": "16.6.8",
4
+ "version": "16.6.11",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/akillness/jeopi",
7
7
  "author": "Can Boluk",
@@ -57,17 +57,17 @@
57
57
  "@agentclientprotocol/sdk": "0.25.0",
58
58
  "@babel/parser": "^7.29.7",
59
59
  "@mozilla/readability": "^0.6.0",
60
- "jeopi-hashline": "16.6.8",
61
- "jeopi-stats": "16.6.8",
62
- "jeopi-agent-core": "16.6.8",
63
- "jeopi-ai": "16.6.8",
64
- "jeopi-catalog": "16.6.8",
65
- "jeopi-mnemopi": "16.6.8",
66
- "jeopi-natives": "16.6.8",
67
- "jeopi-tui": "16.6.8",
68
- "jeopi-utils": "16.6.8",
69
- "jeopi-wire": "16.6.8",
70
- "jeopi-snapcompact": "16.6.8",
60
+ "jeopi-hashline": "16.6.11",
61
+ "jeopi-stats": "16.6.11",
62
+ "jeopi-agent-core": "16.6.11",
63
+ "jeopi-ai": "16.6.11",
64
+ "jeopi-catalog": "16.6.11",
65
+ "jeopi-mnemopi": "16.6.11",
66
+ "jeopi-natives": "16.6.11",
67
+ "jeopi-tui": "16.6.11",
68
+ "jeopi-utils": "16.6.11",
69
+ "jeopi-wire": "16.6.11",
70
+ "jeopi-snapcompact": "16.6.11",
71
71
  "@opentelemetry/api": "^1.9.1",
72
72
  "@opentelemetry/context-async-hooks": "^2.7.1",
73
73
  "@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
@@ -155,11 +155,13 @@ export function mergeDiscoveredModel<TApi extends Api>(
155
155
  return model;
156
156
  }
157
157
 
158
- const AUTHORITATIVE_RUNTIME_CATALOG_PROVIDERS = new Set<string>(
159
- PROVIDER_DESCRIPTORS.filter(descriptor => descriptor.dynamicModelsAuthoritative).map(
158
+ const AUTHORITATIVE_RUNTIME_CATALOG_PROVIDERS = new Set<string>([
159
+ ...PROVIDER_DESCRIPTORS.filter(descriptor => descriptor.dynamicModelsAuthoritative).map(
160
160
  descriptor => descriptor.providerId,
161
161
  ),
162
- );
162
+ "google-antigravity",
163
+ "openai-codex",
164
+ ]);
163
165
 
164
166
  function isAuthoritativeLocalDiscovery(providerConfig: DiscoveryProviderConfig): boolean {
165
167
  switch (providerConfig.discovery.type) {
@@ -1066,14 +1068,48 @@ export class ModelRegistry {
1066
1068
  });
1067
1069
  }
1068
1070
 
1071
+ #resolveGoogleAntigravityCacheContext() {
1072
+ const account = this.authStorage.listOAuthAccounts("google-antigravity")[0];
1073
+ if (account) {
1074
+ return {
1075
+ account,
1076
+ cacheScope: JSON.stringify([account.credentialId, account.accountId ?? account.email, account.projectId]),
1077
+ };
1078
+ }
1079
+ const origin = this.authStorage.getCredentialOrigin("google-antigravity")?.kind;
1080
+ return {
1081
+ account: undefined,
1082
+ cacheScope:
1083
+ origin === "runtime" || origin === "config"
1084
+ ? this.authStorage.getModelCacheIdentity("google-antigravity")
1085
+ : undefined,
1086
+ };
1087
+ }
1088
+
1069
1089
  #resolveStartupModelCacheProviderId(providerId: string): string {
1090
+ const baseUrl =
1091
+ this.#runtimeProviderOverrides.get(providerId)?.baseUrl ??
1092
+ this.#providerOverrides.get(providerId)?.baseUrl ??
1093
+ this.getProviderBaseUrl(providerId) ??
1094
+ getBundledModels(providerId as GeneratedProvider).find(m => m.baseUrl)?.baseUrl;
1095
+ if (providerId === "google-antigravity") {
1096
+ const commandKey = this.#resolveCommandBackedApiKey(providerId);
1097
+ const oauthToken = commandKey.configured
1098
+ ? extractGoogleOAuthToken(commandKey.value)
1099
+ : this.authStorage.getOAuthCredential("google-antigravity")?.access;
1100
+ const { cacheScope } = this.#resolveGoogleAntigravityCacheContext();
1101
+ return (
1102
+ googleAntigravityModelManagerOptions({
1103
+ oauthToken,
1104
+ cacheScope,
1105
+ endpoint: baseUrl,
1106
+ fetch: this.#fetch,
1107
+ }).cacheProviderId ?? providerId
1108
+ );
1109
+ }
1070
1110
  if (providerId === "openai" || providerId === "openai-codex") {
1071
1111
  const commandKey = this.#resolveCommandBackedApiKey(providerId);
1072
1112
  const apiKey = commandKey.configured ? commandKey.value : this.authStorage.getModelCacheIdentity(providerId);
1073
- const baseUrl =
1074
- this.#runtimeProviderOverrides.get(providerId)?.baseUrl ??
1075
- this.#providerOverrides.get(providerId)?.baseUrl ??
1076
- this.getProviderBaseUrl(providerId);
1077
1113
  if (providerId === "openai-codex") {
1078
1114
  return (
1079
1115
  openaiCodexModelManagerOptions({
@@ -1089,10 +1125,6 @@ export class ModelRegistry {
1089
1125
  if (!descriptor) {
1090
1126
  return providerId;
1091
1127
  }
1092
- const baseUrl =
1093
- this.#runtimeProviderOverrides.get(providerId)?.baseUrl ??
1094
- this.#providerOverrides.get(providerId)?.baseUrl ??
1095
- this.getProviderBaseUrl(providerId);
1096
1128
  return descriptor.createModelManagerOptions({ baseUrl, fetch: this.#fetch }).cacheProviderId ?? providerId;
1097
1129
  }
1098
1130
 
@@ -1679,10 +1711,7 @@ export class ModelRegistry {
1679
1711
  createOptions: oauthToken => {
1680
1712
  // Pin discovery to one stable account, including when its access token has expired.
1681
1713
  // Independent peek/getApiKey calls otherwise advance round-robin and poison the cache scope.
1682
- const account = this.authStorage.listOAuthAccounts("google-antigravity")[0];
1683
- const cacheScope = account
1684
- ? JSON.stringify([account.credentialId, account.accountId ?? account.email, account.projectId])
1685
- : undefined;
1714
+ const { account, cacheScope } = this.#resolveGoogleAntigravityCacheContext();
1686
1715
  return googleAntigravityModelManagerOptions({
1687
1716
  oauthToken,
1688
1717
  cacheScope,
@@ -1785,7 +1814,11 @@ export class ModelRegistry {
1785
1814
  options.push(descriptor.createOptions(key));
1786
1815
  }
1787
1816
  for (const option of options) {
1788
- if (option.providerId === "openai" || option.providerId === "openai-codex") {
1817
+ if (
1818
+ option.providerId === "openai" ||
1819
+ option.providerId === "openai-codex" ||
1820
+ option.providerId === "google-antigravity"
1821
+ ) {
1789
1822
  option.cacheProviderId = this.#resolveStartupModelCacheProviderId(option.providerId);
1790
1823
  }
1791
1824
  }
@@ -2043,7 +2076,7 @@ export class ModelRegistry {
2043
2076
  * Find a model by provider and ID.
2044
2077
  */
2045
2078
  find(provider: string, modelId: string): Model<Api> | undefined {
2046
- return resolveProviderModelReference(provider, modelId, this.#models);
2079
+ return resolveProviderModelReference(provider, modelId, this.getAll());
2047
2080
  }
2048
2081
 
2049
2082
  /**