@cjhyy/code-shell-core 0.8.13 → 0.8.20
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/dist/cli/agent-server-stdio.js +4 -3
- package/dist/engine/engine.js +24 -11
- package/dist/engine/run-session-open.js +1 -1
- package/dist/engine/run-types.d.ts +2 -0
- package/dist/engine/turn-loop.js +17 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.internal.d.ts +1 -0
- package/dist/index.internal.js +1 -0
- package/dist/index.js +2 -2
- package/dist/llm/client-base.d.ts +2 -1
- package/dist/llm/client-base.js +3 -1
- package/dist/llm/providers/anthropic.js +24 -25
- package/dist/llm/providers/openai.d.ts +4 -1
- package/dist/llm/providers/openai.js +76 -13
- package/dist/panel-apps/index.d.ts +1 -1
- package/dist/panel-apps/index.js +1 -1
- package/dist/panel-apps/installer.d.ts +29 -0
- package/dist/panel-apps/installer.js +124 -15
- package/dist/plugins/pluginCatalog.d.ts +6 -0
- package/dist/plugins/pluginCatalog.js +7 -2
- package/dist/plugins/pluginContent.d.ts +1 -1
- package/dist/plugins/pluginContent.js +2 -10
- package/dist/protocol/chat-session.d.ts +2 -0
- package/dist/protocol/server.js +7 -0
- package/dist/protocol/types.d.ts +2 -0
- package/dist/session/session-manager.d.ts +7 -0
- package/dist/session/session-manager.js +19 -6
- package/dist/session/transcript.d.ts +9 -0
- package/dist/session/transcript.js +81 -0
- package/dist/settings/manager.d.ts +12 -0
- package/dist/settings/manager.js +31 -0
- package/dist/tool-system/builtin/configure-model-connection.d.ts +36 -0
- package/dist/tool-system/builtin/configure-model-connection.js +396 -0
- package/dist/tool-system/builtin/edit-model-catalog.d.ts +4 -6
- package/dist/tool-system/builtin/edit-model-catalog.js +5 -8
- package/dist/tool-system/builtin/index.js +14 -0
- package/dist/tool-system/builtin/install-capability.js +22 -8
- package/dist/tool-system/builtin/settings-changed.d.ts +9 -0
- package/dist/tool-system/builtin/settings-changed.js +18 -0
- package/dist/tool-system/context.d.ts +8 -2
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
|
@@ -333,9 +333,10 @@ setDefaultCredentialAccess(createIpcCredentialAccess(stdioTransport));
|
|
|
333
333
|
setCronChangedSink(() => {
|
|
334
334
|
stdioTransport.send(createNotification("agent/cronChanged", {}));
|
|
335
335
|
});
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
// after a successful write instead of relying on a later
|
|
336
|
+
// Catalog and model-connection tools write in this worker process, while the
|
|
337
|
+
// mounted settings/connection pages live in the desktop renderer. Notify them
|
|
338
|
+
// immediately after a successful write instead of relying on a later
|
|
339
|
+
// turn_complete event.
|
|
339
340
|
setModelCatalogChangedSink(() => {
|
|
340
341
|
stdioTransport.send(createNotification(Methods.SettingsChanged, {}));
|
|
341
342
|
});
|
package/dist/engine/engine.js
CHANGED
|
@@ -1104,7 +1104,7 @@ export class Engine {
|
|
|
1104
1104
|
// the entire old topic. Fail open to full history if summarization fails.
|
|
1105
1105
|
if (options?.archiveBeforeCurrentTurn && options.clientMessageId) {
|
|
1106
1106
|
try {
|
|
1107
|
-
const
|
|
1107
|
+
const anchors = {
|
|
1108
1108
|
toClientMessageId: options.clientMessageId,
|
|
1109
1109
|
...(options.archiveBeforeCurrentTurn.fromClientMessageId
|
|
1110
1110
|
? {
|
|
@@ -1114,7 +1114,24 @@ export class Engine {
|
|
|
1114
1114
|
...(options.archiveBeforeCurrentTurn.segmentId
|
|
1115
1115
|
? { segmentId: options.archiveBeforeCurrentTurn.segmentId }
|
|
1116
1116
|
: {}),
|
|
1117
|
-
}
|
|
1117
|
+
};
|
|
1118
|
+
const before = estimateTokens(messages);
|
|
1119
|
+
if (options.archiveBeforeCurrentTurn.summary) {
|
|
1120
|
+
await this.appendArchiveMarker(session.state.sessionId, {
|
|
1121
|
+
...anchors,
|
|
1122
|
+
summary: options.archiveBeforeCurrentTurn.summary,
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
else {
|
|
1126
|
+
await this.archiveTurnRange(session.state.sessionId, { start: 0, end: 0 }, anchors);
|
|
1127
|
+
}
|
|
1128
|
+
// openRunSession captured the pre-marker replay. Rebuild from the
|
|
1129
|
+
// persisted marker so this very first post-boundary model call gets
|
|
1130
|
+
// the archived view rather than waiting until the following turn.
|
|
1131
|
+
messages =
|
|
1132
|
+
this.compactedMessagesBySession.get(session.state.sessionId) ??
|
|
1133
|
+
this.sessionManager.resumeForRun(session.state.sessionId).transcript.toMessages();
|
|
1134
|
+
const archived = { before, after: estimateTokens(messages) };
|
|
1118
1135
|
if (archived.before > archived.after) {
|
|
1119
1136
|
options.onStream?.({
|
|
1120
1137
|
type: "context_compact",
|
|
@@ -1123,12 +1140,6 @@ export class Engine {
|
|
|
1123
1140
|
after: archived.after,
|
|
1124
1141
|
});
|
|
1125
1142
|
}
|
|
1126
|
-
// openRunSession captured the pre-marker replay. Rebuild from the
|
|
1127
|
-
// persisted marker so this very first post-boundary model call gets
|
|
1128
|
-
// the archived view rather than waiting until the following turn.
|
|
1129
|
-
messages =
|
|
1130
|
-
this.compactedMessagesBySession.get(session.state.sessionId) ??
|
|
1131
|
-
this.sessionManager.resume(session.state.sessionId).transcript.toMessages();
|
|
1132
1143
|
}
|
|
1133
1144
|
catch (error) {
|
|
1134
1145
|
logger.warn("engine.pre_run_archive.failed", {
|
|
@@ -2018,9 +2029,11 @@ export class Engine {
|
|
|
2018
2029
|
runAllowedToolNames.has("cancel_goal")
|
|
2019
2030
|
? { hasGoal: hasRunnableGoal }
|
|
2020
2031
|
: undefined,
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2032
|
+
// Static sections named by the active preset are part of that preset's
|
|
2033
|
+
// contract. `disableCapabilityContext` suppresses ambient/dynamic
|
|
2034
|
+
// capability context, but removing these definitions leaves presets
|
|
2035
|
+
// with dangling section names (for example terminal-coding → coding).
|
|
2036
|
+
capabilityPromptSections: this.capabilityPromptSections,
|
|
2024
2037
|
dynamicContextProviders: this.capabilityDynamicContextProviders,
|
|
2025
2038
|
getSettingsManager: () => this.getSettingsManager(),
|
|
2026
2039
|
toolCatalog: this.toolCatalog,
|
|
@@ -29,7 +29,7 @@ export function openRunSession(args) {
|
|
|
29
29
|
};
|
|
30
30
|
if (options?.sessionId && args.sessionManager.exists(options.sessionId)) {
|
|
31
31
|
resumedFromDisk = true;
|
|
32
|
-
session = args.sessionManager.
|
|
32
|
+
session = args.sessionManager.resumeForRun(options.sessionId);
|
|
33
33
|
const cachedCompacted = args.cachedCompactedMessages;
|
|
34
34
|
messages = cachedCompacted ? [...cachedCompacted] : session.transcript.toMessages();
|
|
35
35
|
// If the previous run was Ctrl+C'd or crashed between an assistant
|
|
@@ -106,6 +106,8 @@ export interface EngineRunOptions {
|
|
|
106
106
|
archiveBeforeCurrentTurn?: {
|
|
107
107
|
fromClientMessageId?: string;
|
|
108
108
|
segmentId?: string;
|
|
109
|
+
/** Host-authored replacement for the archived span; skips the summarizer. */
|
|
110
|
+
summary?: string;
|
|
109
111
|
};
|
|
110
112
|
attachments?: InputAttachmentMeta[];
|
|
111
113
|
/** Named per-run behavior profile supplied by interactive product surfaces. */
|
package/dist/engine/turn-loop.js
CHANGED
|
@@ -836,6 +836,23 @@ export class TurnLoop {
|
|
|
836
836
|
if (response.text) {
|
|
837
837
|
messages.push({ role: "assistant", content: response.text });
|
|
838
838
|
}
|
|
839
|
+
// Anthropic streams tool_use_start as soon as the content block
|
|
840
|
+
// opens. If max_tokens cuts the JSON arguments off, we deliberately
|
|
841
|
+
// do not execute that call, but the renderer still needs a terminal
|
|
842
|
+
// event for the already-open card. Without it Claude tool cards stay
|
|
843
|
+
// "working" forever (often until a later context compaction hides
|
|
844
|
+
// them), even though the loop has moved on to a retry.
|
|
845
|
+
for (const toolCall of response.toolCalls) {
|
|
846
|
+
this.config.onStream?.({
|
|
847
|
+
type: "tool_result",
|
|
848
|
+
result: {
|
|
849
|
+
id: toolCall.id,
|
|
850
|
+
toolName: toolCall.toolName,
|
|
851
|
+
error: "Tool call was not executed because its arguments were truncated.",
|
|
852
|
+
isError: true,
|
|
853
|
+
},
|
|
854
|
+
});
|
|
855
|
+
}
|
|
839
856
|
messages.push({
|
|
840
857
|
role: "user",
|
|
841
858
|
content: "<system-reminder>Your previous response was truncated by the max output token limit before the tool call finished, so its arguments are incomplete. Do not assume it ran. Either retry with a smaller/more focused tool call (e.g. write the file in sections via Edit), or raise this model's maxOutputTokens.</system-reminder>",
|
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export declare const VERSION = "0.8.
|
|
6
|
+
export declare const VERSION = "0.8.20";
|
|
7
7
|
export type { Message, ContentBlock, ToolDefinition, ToolCall, ToolResult, RegisteredTool, TranscriptEvent, TranscriptEventType, SessionState, SessionKind, SessionWorkspace, SessionForkLineage, ContextUsageAnchor, SessionStatus, TokenUsage, CompiledInput, PermissionDecision, PermissionMode, PermissionRule, TurnPhase, TurnResult, TerminalReason, TurnCompletionKind, StreamEvent, StreamCallback, LLMConfig, ClientDefaults, LLMResponse, Settings, MCPServerConfig, } from "./types.js";
|
|
8
8
|
export type { GoalConfig, GoalLifecycleConfig, GoalLifecyclePhase, GoalLifecycleTerminalReason, GoalLifecycleV1, } from "./goal/lifecycle.js";
|
|
9
9
|
export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, CompositionError, SandboxUnavailableError, } from "./exceptions.js";
|
|
@@ -98,7 +98,7 @@ export { mergePluginMcpServers, readPluginMcp } from "./plugins/installer/loadPl
|
|
|
98
98
|
export { approvePluginMcp, listPluginMcpTrust, revokePluginMcp, type PluginMcpApprovalResult, type PluginMcpTrustEntry, } from "./plugins/pluginMcpApproval.js";
|
|
99
99
|
export { pluginMcpApprovalState, type PluginMcpApprovalState, } from "./plugins/pluginMcpIntegrity.js";
|
|
100
100
|
export { pluginsRoot } from "./plugins/installer/paths.js";
|
|
101
|
-
export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppManifest, PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, previewLocalPanelApp, previewInstalledPanelAppUpdate, isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, uninstallPanelApp, type InstalledPanelApp, type InstalledPanelAppSource, type GitPanelAppSourceInput, type LocalPanelAppSourceInput, type PanelAppSourceInput, type PanelAppManifestData, type PanelAppBindingPolicy, type PanelAppPreview, } from "./panel-apps/index.js";
|
|
101
|
+
export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppManifest, PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, discoverGitPanelApps, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, previewLocalPanelApp, previewInstalledPanelAppUpdate, isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, uninstallPanelApp, type InstalledPanelApp, type InstalledPanelAppSource, type GitPanelAppDiscovery, type GitPanelAppDiscoveryCandidate, type GitPanelAppDiscoveryIssue, type GitPanelAppSourceInput, type LocalPanelAppSourceInput, type PanelAppSourceInput, type PanelAppManifestData, type PanelAppBindingPolicy, type PanelAppPreview, } from "./panel-apps/index.js";
|
|
102
102
|
export { previewLocalTheme, installReviewedLocalTheme, listInstalledThemes, uninstallTheme, themesRoot, themeInstallDir, assertSafeThemeName, ThemeInstallError, ThemeReviewChangedError, THEME_ASSET_DIR, THEME_VAR_NAMES, detectThemeImage, type ThemePreview, type InstalledTheme, type ThemeManifest, } from "./themes/index.js";
|
|
103
103
|
export { resolveSafePluginPath } from "./plugins/pluginInstaller.js";
|
|
104
104
|
export { listPluginHooks, pluginHookKey, type PluginHookEntry } from "./plugins/loadPluginHooks.js";
|
package/dist/index.internal.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ export { recordUIEvent } from "./logging/session-recorder.js";
|
|
|
26
26
|
export { getInteractiveApprovalBackend } from "./tool-system/permission.js";
|
|
27
27
|
export { defaultSandboxConfig, type SandboxConfig } from "./tool-system/sandbox/index.js";
|
|
28
28
|
export { buildNotificationMessage, buildNotificationSummary, notificationQueue, agentNotificationBus, notificationItemToStreamEvent, type NotificationItem, } from "./tool-system/builtin/agent-notifications.js";
|
|
29
|
+
export { backgroundJobRegistry } from "./tool-system/builtin/background-jobs.js";
|
|
29
30
|
export type { BackgroundAgentCompletedEvent } from "./types.js";
|
|
30
31
|
export { startAutomation, type StartAutomationDeps, type AutomationHandle, CronScheduler, cronScheduler, type CronExecutionOutcome, type CronJob, type CronJobLifecycleEvent, type CronPermissionLevel, type CronTemplateSource, type CreateJobOptions, type UpdateJobPatch, CronStore, defaultCronStorePath, bindCronToEngine, bindCronToRunManager, type CronRunner, type CronRunRequest, type CronRunResult, type RunSubmitter, isCronExpression, parseCronExpression, nextCronTime, validateSchedule, type ParsedCron, resolveWritePolicy, wrapUntrustedInput, type WritePolicy, runWriteJobInWorktree, type WriteJobGitOps, type RunWriteJobInput, type RunWriteJobResult, } from "./automation/index.js";
|
|
31
32
|
export { asyncAgentRegistry, type AsyncAgentEntry } from "./tool-system/builtin/agent-registry.js";
|
package/dist/index.internal.js
CHANGED
|
@@ -29,6 +29,7 @@ export { recordUIEvent } from "./logging/session-recorder.js";
|
|
|
29
29
|
export { getInteractiveApprovalBackend } from "./tool-system/permission.js";
|
|
30
30
|
export { defaultSandboxConfig } from "./tool-system/sandbox/index.js";
|
|
31
31
|
export { buildNotificationMessage, buildNotificationSummary, notificationQueue, agentNotificationBus, notificationItemToStreamEvent, } from "./tool-system/builtin/agent-notifications.js";
|
|
32
|
+
export { backgroundJobRegistry } from "./tool-system/builtin/background-jobs.js";
|
|
32
33
|
export { startAutomation, CronScheduler, cronScheduler, CronStore, defaultCronStorePath, bindCronToEngine, bindCronToRunManager, isCronExpression, parseCronExpression, nextCronTime, validateSchedule, resolveWritePolicy, wrapUntrustedInput, runWriteJobInWorktree, } from "./automation/index.js";
|
|
33
34
|
export { asyncAgentRegistry } from "./tool-system/builtin/agent-registry.js";
|
|
34
35
|
export { backgroundShellManager, BackgroundShellManager, } from "./runtime/background-shell.js";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export const VERSION = "0.8.
|
|
6
|
+
export const VERSION = "0.8.20";
|
|
7
7
|
// ─── Exceptions ──────────────────────────────────────────────────
|
|
8
8
|
export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, CompositionError, SandboxUnavailableError, } from "./exceptions.js";
|
|
9
9
|
// ─── Composition (AgentModule / ResolvedComposition) ─────────────
|
|
@@ -82,7 +82,7 @@ export { mergePluginMcpServers, readPluginMcp } from "./plugins/installer/loadPl
|
|
|
82
82
|
export { approvePluginMcp, listPluginMcpTrust, revokePluginMcp, } from "./plugins/pluginMcpApproval.js";
|
|
83
83
|
export { pluginMcpApprovalState, } from "./plugins/pluginMcpIntegrity.js";
|
|
84
84
|
export { pluginsRoot } from "./plugins/installer/paths.js";
|
|
85
|
-
export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppManifest, PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, previewLocalPanelApp, previewInstalledPanelAppUpdate, isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, uninstallPanelApp, } from "./panel-apps/index.js";
|
|
85
|
+
export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppManifest, PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, discoverGitPanelApps, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, previewLocalPanelApp, previewInstalledPanelAppUpdate, isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, uninstallPanelApp, } from "./panel-apps/index.js";
|
|
86
86
|
export { previewLocalTheme, installReviewedLocalTheme, listInstalledThemes, uninstallTheme, themesRoot, themeInstallDir, assertSafeThemeName, ThemeInstallError, ThemeReviewChangedError, THEME_ASSET_DIR, THEME_VAR_NAMES, detectThemeImage, } from "./themes/index.js";
|
|
87
87
|
export { resolveSafePluginPath } from "./plugins/pluginInstaller.js";
|
|
88
88
|
export { listPluginHooks, pluginHookKey } from "./plugins/loadPluginHooks.js";
|
|
@@ -12,6 +12,7 @@ export declare abstract class LLMClientBase {
|
|
|
12
12
|
readonly timeout: number;
|
|
13
13
|
readonly retryMaxAttempts: number;
|
|
14
14
|
readonly imageDetail?: ClientDefaults["imageDetail"];
|
|
15
|
+
protected readonly fetch?: ClientDefaults["fetch"];
|
|
15
16
|
/**
|
|
16
17
|
* Process-wide hook fired on every LLM response. The CLI installs this in
|
|
17
18
|
* main.ts to feed the cost tracker; lives on the base class so every code
|
|
@@ -23,7 +24,7 @@ export declare abstract class LLMClientBase {
|
|
|
23
24
|
/**
|
|
24
25
|
* `config` carries model identity (provider/model/apiKey/baseUrl/maxTokens/
|
|
25
26
|
* thinking/providerKind). `defaults` carries cross-model runtime knobs
|
|
26
|
-
* (temperature/timeout/retryMaxAttempts/imageDetail) — those are owned by
|
|
27
|
+
* (temperature/timeout/retryMaxAttempts/imageDetail/fetch) — those are owned by
|
|
27
28
|
* the Engine and stay stable across hot model switches.
|
|
28
29
|
*/
|
|
29
30
|
constructor(config: LLMConfig, defaults?: ClientDefaults);
|
package/dist/llm/client-base.js
CHANGED
|
@@ -23,6 +23,7 @@ export class LLMClientBase {
|
|
|
23
23
|
timeout;
|
|
24
24
|
retryMaxAttempts;
|
|
25
25
|
imageDetail;
|
|
26
|
+
fetch;
|
|
26
27
|
/**
|
|
27
28
|
* Process-wide hook fired on every LLM response. The CLI installs this in
|
|
28
29
|
* main.ts to feed the cost tracker; lives on the base class so every code
|
|
@@ -34,7 +35,7 @@ export class LLMClientBase {
|
|
|
34
35
|
/**
|
|
35
36
|
* `config` carries model identity (provider/model/apiKey/baseUrl/maxTokens/
|
|
36
37
|
* thinking/providerKind). `defaults` carries cross-model runtime knobs
|
|
37
|
-
* (temperature/timeout/retryMaxAttempts/imageDetail) — those are owned by
|
|
38
|
+
* (temperature/timeout/retryMaxAttempts/imageDetail/fetch) — those are owned by
|
|
38
39
|
* the Engine and stay stable across hot model switches.
|
|
39
40
|
*/
|
|
40
41
|
constructor(config, defaults) {
|
|
@@ -51,6 +52,7 @@ export class LLMClientBase {
|
|
|
51
52
|
this.timeout = defaults?.timeout ?? 120_000;
|
|
52
53
|
this.retryMaxAttempts = defaults?.retryMaxAttempts ?? 3;
|
|
53
54
|
this.imageDetail = defaults?.imageDetail;
|
|
55
|
+
this.fetch = defaults?.fetch;
|
|
54
56
|
this.initClient();
|
|
55
57
|
}
|
|
56
58
|
recordUsage(usage, options) {
|
|
@@ -21,6 +21,25 @@ const ANTHROPIC_FALLBACK_MAX_TOKENS = 4096;
|
|
|
21
21
|
* no explicit token budget was given. Clamped up to the model's minimum.
|
|
22
22
|
*/
|
|
23
23
|
const ANTHROPIC_DEFAULT_THINKING_BUDGET = 4096;
|
|
24
|
+
/**
|
|
25
|
+
* Anthropic reports uncached input, cache writes, and cache reads as three
|
|
26
|
+
* disjoint counters. `TokenUsage.promptTokens` is the whole prompt throughout
|
|
27
|
+
* CodeShell (OpenAI includes cached tokens in prompt_tokens), so normalize the
|
|
28
|
+
* Anthropic shape before context accounting, compaction, cache-hit math, and
|
|
29
|
+
* cost tracking consume it.
|
|
30
|
+
*/
|
|
31
|
+
function tokenUsageFromAnthropic(usage) {
|
|
32
|
+
const cacheReadTokens = usage.cache_read_input_tokens ?? 0;
|
|
33
|
+
const cacheCreationTokens = usage.cache_creation_input_tokens ?? 0;
|
|
34
|
+
const promptTokens = usage.input_tokens + cacheReadTokens + cacheCreationTokens;
|
|
35
|
+
return {
|
|
36
|
+
promptTokens,
|
|
37
|
+
completionTokens: usage.output_tokens,
|
|
38
|
+
totalTokens: promptTokens + usage.output_tokens,
|
|
39
|
+
...(usage.cache_read_input_tokens != null ? { cacheReadTokens } : {}),
|
|
40
|
+
...(usage.cache_creation_input_tokens != null ? { cacheCreationTokens } : {}),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
24
43
|
export class AnthropicClient extends LLMClientBase {
|
|
25
44
|
_client = null;
|
|
26
45
|
constructor(config, defaults) {
|
|
@@ -37,6 +56,7 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
37
56
|
apiKey: resolveApiKey(this.config, process.env.ANTHROPIC_API_KEY),
|
|
38
57
|
...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
|
|
39
58
|
...(Object.keys(headers).length > 0 ? { defaultHeaders: headers } : {}),
|
|
59
|
+
...(this.fetch ? { fetch: this.fetch } : {}),
|
|
40
60
|
timeout: this.timeout,
|
|
41
61
|
});
|
|
42
62
|
}
|
|
@@ -106,9 +126,7 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
106
126
|
if (ceiling < min) {
|
|
107
127
|
return undefined;
|
|
108
128
|
}
|
|
109
|
-
let budget = reasoning.mode === "budget"
|
|
110
|
-
? reasoning.budgetTokens
|
|
111
|
-
: ANTHROPIC_DEFAULT_THINKING_BUDGET; // "on" or "effort" → default budget
|
|
129
|
+
let budget = reasoning.mode === "budget" ? reasoning.budgetTokens : ANTHROPIC_DEFAULT_THINKING_BUDGET; // "on" or "effort" → default budget
|
|
112
130
|
// Clamp into [min, ceiling] — both bounds are now guaranteed ≥ min.
|
|
113
131
|
budget = Math.min(Math.max(budget, min), ceiling);
|
|
114
132
|
return { type: "enabled", budget_tokens: budget };
|
|
@@ -172,13 +190,7 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
172
190
|
? { temperature: options.temperature }
|
|
173
191
|
: { temperature: this.temperature }),
|
|
174
192
|
}, { signal: requestSignal ?? options.signal });
|
|
175
|
-
const usage =
|
|
176
|
-
promptTokens: response.usage.input_tokens,
|
|
177
|
-
completionTokens: response.usage.output_tokens,
|
|
178
|
-
totalTokens: response.usage.input_tokens + response.usage.output_tokens,
|
|
179
|
-
cacheReadTokens: response.usage.cache_read_input_tokens,
|
|
180
|
-
cacheCreationTokens: response.usage.cache_creation_input_tokens,
|
|
181
|
-
};
|
|
193
|
+
const usage = tokenUsageFromAnthropic(response.usage);
|
|
182
194
|
this.recordUsage(usage, options);
|
|
183
195
|
return this.processResponse(response, usage);
|
|
184
196
|
}
|
|
@@ -209,10 +221,8 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
209
221
|
? { temperature: options.temperature }
|
|
210
222
|
: { temperature: this.temperature }),
|
|
211
223
|
}, { signal: requestSignal ?? options.signal });
|
|
212
|
-
let currentText = "";
|
|
213
224
|
let currentToolName = "";
|
|
214
225
|
let currentToolId = "";
|
|
215
|
-
let currentToolInput = "";
|
|
216
226
|
// Abort-guarded emit: once the turn is cancelled, stop forwarding chunks
|
|
217
227
|
// to the UI. The SDK's event emitter can keep firing buffered text/
|
|
218
228
|
// contentBlock/inputJson events after abort() until its HTTP stream tears
|
|
@@ -249,14 +259,12 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
249
259
|
ttft_ms: Date.now() - streamStartedAt,
|
|
250
260
|
});
|
|
251
261
|
}
|
|
252
|
-
currentText += text;
|
|
253
262
|
emit({ type: "text", text, tokens: countTokens(text) });
|
|
254
263
|
});
|
|
255
264
|
stream.on("contentBlock", (block) => {
|
|
256
265
|
if (block.type === "tool_use") {
|
|
257
266
|
currentToolName = block.name;
|
|
258
267
|
currentToolId = block.id;
|
|
259
|
-
currentToolInput = "";
|
|
260
268
|
emit({
|
|
261
269
|
type: "tool_use_start",
|
|
262
270
|
toolCall: { id: block.id, toolName: block.name, args: {} },
|
|
@@ -264,7 +272,6 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
264
272
|
}
|
|
265
273
|
});
|
|
266
274
|
stream.on("inputJson", (_delta, snapshot) => {
|
|
267
|
-
currentToolInput = JSON.stringify(snapshot);
|
|
268
275
|
if (currentToolId) {
|
|
269
276
|
emit({
|
|
270
277
|
type: "tool_use_delta",
|
|
@@ -277,13 +284,7 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
277
284
|
}
|
|
278
285
|
});
|
|
279
286
|
const finalMessage = await stream.finalMessage();
|
|
280
|
-
const usage =
|
|
281
|
-
promptTokens: finalMessage.usage.input_tokens,
|
|
282
|
-
completionTokens: finalMessage.usage.output_tokens,
|
|
283
|
-
totalTokens: finalMessage.usage.input_tokens + finalMessage.usage.output_tokens,
|
|
284
|
-
cacheReadTokens: finalMessage.usage.cache_read_input_tokens,
|
|
285
|
-
cacheCreationTokens: finalMessage.usage.cache_creation_input_tokens,
|
|
286
|
-
};
|
|
287
|
+
const usage = tokenUsageFromAnthropic(finalMessage.usage);
|
|
287
288
|
this.recordUsage(usage, options);
|
|
288
289
|
options.onChunk?.({ type: "stop", stopReason: finalMessage.stop_reason ?? undefined });
|
|
289
290
|
return this.processResponse(finalMessage, usage);
|
|
@@ -415,9 +416,7 @@ export class AnthropicClient extends LLMClientBase {
|
|
|
415
416
|
// marking them would 400. buildMessages never emits them today (thinking
|
|
416
417
|
// is a top-level request field, not history content), but guard anyway
|
|
417
418
|
// so a future block type can't silently break the request.
|
|
418
|
-
if (lastBlock &&
|
|
419
|
-
lastBlock.type !== "thinking" &&
|
|
420
|
-
lastBlock.type !== "redacted_thinking") {
|
|
419
|
+
if (lastBlock && lastBlock.type !== "thinking" && lastBlock.type !== "redacted_thinking") {
|
|
421
420
|
lastBlock.cache_control = { type: "ephemeral" };
|
|
422
421
|
}
|
|
423
422
|
}
|
|
@@ -49,9 +49,12 @@ interface RunStreamOpts {
|
|
|
49
49
|
export declare function runStreamWithWatchdog<T = any>(stream: AsyncIterable<T>, opts?: RunStreamOpts): Promise<string>;
|
|
50
50
|
export declare class OpenAIClient extends LLMClientBase {
|
|
51
51
|
private _client;
|
|
52
|
+
private readonly dangerouslyAllowBrowser;
|
|
52
53
|
private _forceMaxCompletionTokens;
|
|
53
54
|
private _dropReasoningEffort;
|
|
54
|
-
constructor(config: LLMConfig, defaults?: ClientDefaults
|
|
55
|
+
constructor(config: LLMConfig, defaults?: ClientDefaults, runtimeOptions?: {
|
|
56
|
+
dangerouslyAllowBrowser?: boolean;
|
|
57
|
+
});
|
|
55
58
|
protected initClient(): void;
|
|
56
59
|
private get client();
|
|
57
60
|
/**
|
|
@@ -41,6 +41,24 @@ function cachedTokensOf(usage) {
|
|
|
41
41
|
out.cacheCreationTokens = details.cache_write_tokens;
|
|
42
42
|
return out;
|
|
43
43
|
}
|
|
44
|
+
/** Read the effective output-token ceiling from a built Chat Completions request. */
|
|
45
|
+
function outputTokenLimitOf(requestBody) {
|
|
46
|
+
const value = requestBody.max_completion_tokens ?? requestBody.max_tokens;
|
|
47
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Some OpenAI-compatible gateways return finish_reason `tool_calls` when the
|
|
51
|
+
* output ceiling cuts a function-argument JSON string off. The malformed JSON
|
|
52
|
+
* plus exact cap usage is the evidence that distinguishes this from an ordinary
|
|
53
|
+
* completed tool call.
|
|
54
|
+
*/
|
|
55
|
+
function didHitToolArgumentOutputLimit(input) {
|
|
56
|
+
return (input.finishReason === "tool_calls" &&
|
|
57
|
+
input.malformedToolArguments &&
|
|
58
|
+
input.outputTokenLimit !== undefined &&
|
|
59
|
+
input.completionTokens !== undefined &&
|
|
60
|
+
input.completionTokens >= input.outputTokenLimit);
|
|
61
|
+
}
|
|
44
62
|
/**
|
|
45
63
|
* Consume an async iterable of stream chunks with an idle watchdog.
|
|
46
64
|
* Returns the accumulated text — either from onChunk return values, or
|
|
@@ -193,6 +211,7 @@ function normalizeOpenAIToolMessagePairs(messages) {
|
|
|
193
211
|
}
|
|
194
212
|
export class OpenAIClient extends LLMClientBase {
|
|
195
213
|
_client = null;
|
|
214
|
+
dangerouslyAllowBrowser;
|
|
196
215
|
// Sticky override: once the endpoint tells us `max_tokens` is rejected for
|
|
197
216
|
// this model, switch to `max_completion_tokens` for the lifetime of the
|
|
198
217
|
// client. Cheaper and more reliable than re-deriving from the model id when
|
|
@@ -205,8 +224,9 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
205
224
|
// succeed. Omitting the field just means "model default reasoning", which is
|
|
206
225
|
// fine for our background/aux calls.
|
|
207
226
|
_dropReasoningEffort = false;
|
|
208
|
-
constructor(config, defaults) {
|
|
227
|
+
constructor(config, defaults, runtimeOptions = {}) {
|
|
209
228
|
super(config, defaults);
|
|
229
|
+
this.dangerouslyAllowBrowser = runtimeOptions.dangerouslyAllowBrowser === true;
|
|
210
230
|
}
|
|
211
231
|
initClient() {
|
|
212
232
|
// Lazy init — client created on first use
|
|
@@ -222,6 +242,8 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
222
242
|
(Object.keys(headers).length > 0 ? "x-headers-auth" : undefined),
|
|
223
243
|
...(this.config.baseUrl ? { baseURL: this.config.baseUrl } : {}),
|
|
224
244
|
...(Object.keys(headers).length > 0 ? { defaultHeaders: headers } : {}),
|
|
245
|
+
...(this.fetch ? { fetch: this.fetch } : {}),
|
|
246
|
+
...(this.dangerouslyAllowBrowser ? { dangerouslyAllowBrowser: true } : {}),
|
|
225
247
|
timeout: this.timeout,
|
|
226
248
|
});
|
|
227
249
|
}
|
|
@@ -473,7 +495,8 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
473
495
|
}
|
|
474
496
|
async nonStreamMessage(options, messages, tools, reasoning, requestSignal) {
|
|
475
497
|
try {
|
|
476
|
-
const
|
|
498
|
+
const requestBody = this.buildRequestBody(options, messages, tools, reasoning, false);
|
|
499
|
+
const response = await this.client.chat.completions.create(requestBody, { signal: requestSignal ?? options.signal });
|
|
477
500
|
const choice = response.choices[0];
|
|
478
501
|
if (!choice)
|
|
479
502
|
throw new LLMError("No response from OpenAI", "openai");
|
|
@@ -486,7 +509,7 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
486
509
|
...cachedTokensOf(response.usage),
|
|
487
510
|
};
|
|
488
511
|
this.recordUsage(usage, options);
|
|
489
|
-
return this.processChoice(choice, usage);
|
|
512
|
+
return this.processChoice(choice, usage, outputTokenLimitOf(requestBody));
|
|
490
513
|
}
|
|
491
514
|
catch (err) {
|
|
492
515
|
this.handleApiError(err);
|
|
@@ -496,7 +519,9 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
496
519
|
async streamMessage(options, messages, tools, reasoning, requestSignal) {
|
|
497
520
|
const sdkSignal = requestSignal ?? options.signal;
|
|
498
521
|
try {
|
|
499
|
-
const
|
|
522
|
+
const requestBody = this.buildRequestBody(options, messages, tools, reasoning, true);
|
|
523
|
+
const stream = await this.client.chat.completions.create(requestBody, { signal: sdkSignal });
|
|
524
|
+
const outputTokenLimit = outputTokenLimitOf(requestBody);
|
|
500
525
|
let text = "";
|
|
501
526
|
let reasoningContent = "";
|
|
502
527
|
const toolCallsMap = new Map();
|
|
@@ -605,6 +630,7 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
605
630
|
signal: sdkSignal,
|
|
606
631
|
});
|
|
607
632
|
const toolCalls = [];
|
|
633
|
+
let malformedToolArguments = false;
|
|
608
634
|
for (const [, tc] of toolCallsMap) {
|
|
609
635
|
// Drop incomplete tool calls: an empty id or name (the fallbacks set
|
|
610
636
|
// when a delta never delivered them) would become a malformed call
|
|
@@ -621,9 +647,16 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
621
647
|
args = JSON.parse(tc.args || "{}");
|
|
622
648
|
}
|
|
623
649
|
catch {
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
650
|
+
malformedToolArguments = true;
|
|
651
|
+
// Keep the historical empty-args fallback for genuinely malformed
|
|
652
|
+
// model output. If usage also proves the configured output ceiling was
|
|
653
|
+
// reached, the stop reason is normalized to "length" below so the turn
|
|
654
|
+
// loop retries instead of executing this misleading empty call.
|
|
655
|
+
logger.warn("openai.malformed_tool_arguments", {
|
|
656
|
+
id: tc.id,
|
|
657
|
+
name: tc.name,
|
|
658
|
+
argumentChars: tc.args.length,
|
|
659
|
+
});
|
|
627
660
|
}
|
|
628
661
|
toolCalls.push({ id: tc.id, toolName: tc.name, args });
|
|
629
662
|
}
|
|
@@ -634,11 +667,31 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
634
667
|
...cachedTokensOf(streamUsage),
|
|
635
668
|
};
|
|
636
669
|
this.recordUsage(usage, options);
|
|
670
|
+
const inferredToolArgumentTruncation = didHitToolArgumentOutputLimit({
|
|
671
|
+
finishReason,
|
|
672
|
+
malformedToolArguments,
|
|
673
|
+
completionTokens: usage.completionTokens,
|
|
674
|
+
outputTokenLimit,
|
|
675
|
+
});
|
|
676
|
+
if (inferredToolArgumentTruncation) {
|
|
677
|
+
logger.warn("openai.tool_arguments_truncation_inferred", {
|
|
678
|
+
provider: this.provider,
|
|
679
|
+
model: this.model,
|
|
680
|
+
finishReason,
|
|
681
|
+
completionTokens: usage.completionTokens,
|
|
682
|
+
outputTokenLimit,
|
|
683
|
+
toolCount: toolCalls.length,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
637
686
|
return {
|
|
638
687
|
text,
|
|
639
688
|
toolCalls,
|
|
640
689
|
usage,
|
|
641
|
-
|
|
690
|
+
// OpenRouter can report `tool_calls` even when max_tokens cuts a tool's
|
|
691
|
+
// argument JSON off. Normalize that proven cap hit to the standard
|
|
692
|
+
// OpenAI `length` spelling so the shared continuation guard can skip
|
|
693
|
+
// execution and ask the model to retry with a smaller call.
|
|
694
|
+
stopReason: inferredToolArgumentTruncation ? "length" : (finishReason ?? "stop"),
|
|
642
695
|
...(reasoningContent ? { reasoningContent } : {}),
|
|
643
696
|
};
|
|
644
697
|
}
|
|
@@ -647,9 +700,10 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
647
700
|
throw err;
|
|
648
701
|
}
|
|
649
702
|
}
|
|
650
|
-
processChoice(choice, usage) {
|
|
703
|
+
processChoice(choice, usage, outputTokenLimit) {
|
|
651
704
|
const text = choice.message.content ?? "";
|
|
652
705
|
const toolCalls = [];
|
|
706
|
+
let malformedToolArguments = false;
|
|
653
707
|
if (choice.message.tool_calls) {
|
|
654
708
|
for (const tc of choice.message.tool_calls) {
|
|
655
709
|
let args = {};
|
|
@@ -657,9 +711,12 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
657
711
|
args = JSON.parse(tc.function.arguments || "{}");
|
|
658
712
|
}
|
|
659
713
|
catch {
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
714
|
+
malformedToolArguments = true;
|
|
715
|
+
logger.warn("openai.malformed_tool_arguments", {
|
|
716
|
+
id: tc.id,
|
|
717
|
+
name: tc.function.name,
|
|
718
|
+
argumentChars: tc.function.arguments?.length ?? 0,
|
|
719
|
+
});
|
|
663
720
|
}
|
|
664
721
|
toolCalls.push({
|
|
665
722
|
id: tc.id,
|
|
@@ -669,11 +726,17 @@ export class OpenAIClient extends LLMClientBase {
|
|
|
669
726
|
}
|
|
670
727
|
}
|
|
671
728
|
const reasoningContent = extractReasoningContent(choice.message);
|
|
729
|
+
const inferredToolArgumentTruncation = didHitToolArgumentOutputLimit({
|
|
730
|
+
finishReason: choice.finish_reason ?? undefined,
|
|
731
|
+
malformedToolArguments,
|
|
732
|
+
completionTokens: usage.completionTokens,
|
|
733
|
+
outputTokenLimit,
|
|
734
|
+
});
|
|
672
735
|
return {
|
|
673
736
|
text,
|
|
674
737
|
toolCalls,
|
|
675
738
|
usage,
|
|
676
|
-
stopReason: choice.finish_reason ?? undefined,
|
|
739
|
+
stopReason: inferredToolArgumentTruncation ? "length" : (choice.finish_reason ?? undefined),
|
|
677
740
|
...(reasoningContent ? { reasoningContent } : {}),
|
|
678
741
|
};
|
|
679
742
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppAgentContribution, PanelAppAgentTool, PanelAppManifest, type PanelAppAgentContribution as PanelAppAgentContributionData, type PanelAppAgentTool as PanelAppAgentToolData, type PanelAppManifest as PanelAppManifestData, } from "./manifest.js";
|
|
2
2
|
export { PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, } from "./paths.js";
|
|
3
|
-
export { installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, previewInstalledPanelAppUpdate, previewLocalPanelApp, uninstallPanelApp, type InstalledPanelApp, type InstalledPanelAppSource, type GitPanelAppSourceInput, type LocalPanelAppSourceInput, type PanelAppSourceInput, type PanelAppPreview, } from "./installer.js";
|
|
3
|
+
export { discoverGitPanelApps, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, previewInstalledPanelAppUpdate, previewLocalPanelApp, uninstallPanelApp, type InstalledPanelApp, type InstalledPanelAppSource, type GitPanelAppSourceInput, type GitPanelAppDiscovery, type GitPanelAppDiscoveryCandidate, type GitPanelAppDiscoveryIssue, type LocalPanelAppSourceInput, type PanelAppSourceInput, type PanelAppPreview, } from "./installer.js";
|
|
4
4
|
export { isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, type PanelAppBindingPolicy, } from "./bindings.js";
|
package/dist/panel-apps/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { PANEL_APP_ICONS, PANEL_APP_MANIFEST_FILE, PANEL_APP_PERMISSIONS, PanelAppAgentContribution, PanelAppAgentTool, PanelAppManifest, } from "./manifest.js";
|
|
2
2
|
export { PanelAppAlreadyInstalledError, PanelAppInstallError, PanelAppReviewChangedError, assertSafePanelAppId, panelAppInstallDir, panelAppsRegistryPath, panelAppsRoot, } from "./paths.js";
|
|
3
|
-
export { installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, previewInstalledPanelAppUpdate, previewLocalPanelApp, uninstallPanelApp, } from "./installer.js";
|
|
3
|
+
export { discoverGitPanelApps, installReviewedLocalPanelApp, installReviewedPanelAppUpdate, listInstalledPanelApps, previewInstalledPanelAppUpdate, previewLocalPanelApp, uninstallPanelApp, } from "./installer.js";
|
|
4
4
|
export { isPanelAppBound, resolvePanelAppBindingPolicy, resolvePanelAppBindingProjectPath, } from "./bindings.js";
|
|
@@ -33,6 +33,29 @@ export interface PanelAppPreview {
|
|
|
33
33
|
};
|
|
34
34
|
warnings: string[];
|
|
35
35
|
}
|
|
36
|
+
export interface GitPanelAppDiscoveryCandidate {
|
|
37
|
+
/** Repository-relative path, or "." when the repository root is the app. */
|
|
38
|
+
subdir: string;
|
|
39
|
+
source: GitPanelAppSourceInput;
|
|
40
|
+
id: string;
|
|
41
|
+
version: string;
|
|
42
|
+
title: {
|
|
43
|
+
default: string;
|
|
44
|
+
en?: string;
|
|
45
|
+
"zh-CN"?: string;
|
|
46
|
+
};
|
|
47
|
+
description?: string;
|
|
48
|
+
icon: PanelAppManifest["icon"];
|
|
49
|
+
}
|
|
50
|
+
export interface GitPanelAppDiscoveryIssue {
|
|
51
|
+
subdir: string;
|
|
52
|
+
error: string;
|
|
53
|
+
}
|
|
54
|
+
export interface GitPanelAppDiscovery {
|
|
55
|
+
source: GitPanelAppSourceInput;
|
|
56
|
+
panels: GitPanelAppDiscoveryCandidate[];
|
|
57
|
+
issues: GitPanelAppDiscoveryIssue[];
|
|
58
|
+
}
|
|
36
59
|
export interface InstalledPanelApp {
|
|
37
60
|
id: string;
|
|
38
61
|
version: string;
|
|
@@ -52,6 +75,12 @@ export interface InstalledPanelApp {
|
|
|
52
75
|
installedAt: string;
|
|
53
76
|
lastUpdated: string;
|
|
54
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Download a public GitHub repository once and enumerate every independently
|
|
80
|
+
* installable Panel App beneath it. Discovery is read-only: selecting a result
|
|
81
|
+
* still runs the normal full review and digest-bound install flow.
|
|
82
|
+
*/
|
|
83
|
+
export declare function discoverGitPanelApps(input: GitPanelAppSourceInput): Promise<GitPanelAppDiscovery>;
|
|
55
84
|
export declare function previewLocalPanelApp(input: PanelAppSourceInput): Promise<PanelAppPreview>;
|
|
56
85
|
/**
|
|
57
86
|
* Re-open the original folder, archive, or GitHub source through the same
|