jeopi-cli 16.6.9 → 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.
- package/CHANGELOG.md +16 -0
- package/dist/cli.js +3369 -3368
- package/dist/types/messenger/live-session.d.ts +28 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -0
- package/dist/types/modes/session-teardown.d.ts +13 -8
- package/dist/types/modes/types.d.ts +2 -0
- package/package.json +12 -12
- package/src/config/model-registry.ts +50 -17
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/messenger/live-session.ts +321 -0
- package/src/modes/interactive-mode.ts +31 -7
- package/src/modes/session-teardown.test.ts +61 -0
- package/src/modes/session-teardown.ts +20 -8
- package/src/modes/types.ts +2 -0
- package/src/sdk.ts +12 -5
- package/src/slash-commands/builtin-registry.ts +104 -0
|
@@ -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
|
|
19
|
-
*
|
|
20
|
-
* after a signal requested shutdown
|
|
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
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "jeopi-cli",
|
|
4
|
-
"version": "16.6.
|
|
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.
|
|
61
|
-
"jeopi-stats": "16.6.
|
|
62
|
-
"jeopi-agent-core": "16.6.
|
|
63
|
-
"jeopi-ai": "16.6.
|
|
64
|
-
"jeopi-catalog": "16.6.
|
|
65
|
-
"jeopi-mnemopi": "16.6.
|
|
66
|
-
"jeopi-natives": "16.6.
|
|
67
|
-
"jeopi-tui": "16.6.
|
|
68
|
-
"jeopi-utils": "16.6.
|
|
69
|
-
"jeopi-wire": "16.6.
|
|
70
|
-
"jeopi-snapcompact": "16.6.
|
|
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
|
|
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 (
|
|
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
|
|
2079
|
+
return resolveProviderModelReference(provider, modelId, this.getAll());
|
|
2047
2080
|
}
|
|
2048
2081
|
|
|
2049
2082
|
/**
|