@sentry/junior 0.116.1 → 0.118.0
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/{agent-hooks-GUAAS3XH.js → agent-hooks-J2FB7XD6.js} +8 -10
- package/dist/api/conversations/detail.d.ts +7 -0
- package/dist/api/conversations/event-list.d.ts +7 -0
- package/dist/api/conversations/events.d.ts +1 -1
- package/dist/api/schema/conversation.d.ts +28 -0
- package/dist/api/schema/stats.d.ts +22 -0
- package/dist/api/schema.d.ts +2 -0
- package/dist/api/schema.js +7 -3
- package/dist/api/stats.d.ts +16 -0
- package/dist/api/user-pages/routes.d.ts +4 -0
- package/dist/api.js +192 -18
- package/dist/app.js +53 -46
- package/dist/{catalog-runtime-WMPC4J4U.js → catalog-runtime-UHHLU53I.js} +5 -5
- package/dist/chat/agent/prompt.d.ts +6 -2
- package/dist/chat/agent/request.d.ts +2 -1
- package/dist/chat/agent/resume.d.ts +2 -0
- package/dist/chat/agent/tools.d.ts +1 -0
- package/dist/chat/conversations/history.d.ts +22 -1
- package/dist/chat/conversations/projection.d.ts +6 -0
- package/dist/chat/mcp/client.d.ts +4 -0
- package/dist/chat/mcp/errors.d.ts +22 -1
- package/dist/chat/oauth-response.d.ts +21 -0
- package/dist/chat/plugins/prompt.d.ts +9 -0
- package/dist/chat/plugins/types.d.ts +2 -21
- package/dist/chat/plugins/user-pages.d.ts +15 -0
- package/dist/chat/plugins/viewer-actors.d.ts +6 -0
- package/dist/chat/prompt.d.ts +1 -2
- package/dist/chat/sandbox/paths.d.ts +2 -0
- package/dist/chat/sandbox/sandbox.d.ts +3 -1
- package/dist/chat/sandbox/snapshot/install.d.ts +6 -0
- package/dist/chat/sandbox/snapshot/profile.d.ts +14 -0
- package/dist/chat/sandbox/snapshot/resolve.d.ts +22 -0
- package/dist/chat/sandbox/snapshot/span.d.ts +2 -0
- package/dist/chat/sandbox/workspace.d.ts +1 -0
- package/dist/chat/services/context-compaction-marker.d.ts +1 -0
- package/dist/chat/services/turn-session-record.d.ts +2 -0
- package/dist/chat/skills.d.ts +1 -2
- package/dist/chat/state/turn-session.d.ts +2 -0
- package/dist/chat/tools/index.d.ts +4 -1
- package/dist/chat/tools/query-conversation-events.d.ts +1 -1
- package/dist/chat/tools/sandbox/file-utils.d.ts +11 -0
- package/dist/chat/tools/sandbox/find-files.d.ts +2 -1
- package/dist/chat/tools/sandbox/grep.d.ts +2 -1
- package/dist/chat/tools/sandbox/view-image.d.ts +11 -0
- package/dist/chat/tools/types.d.ts +7 -0
- package/dist/{chunk-XA3VLFPK.js → chunk-3LGCVRBQ.js} +154 -20
- package/dist/{chunk-JPKGW6L6.js → chunk-3MUSSFMP.js} +78 -39
- package/dist/{chunk-7CSVG54J.js → chunk-3N3FMCKN.js} +30 -2
- package/dist/{chunk-VBMMMVVQ.js → chunk-4LPPH7Y6.js} +405 -446
- package/dist/{chunk-2YYPXQNQ.js → chunk-4SOQPUF4.js} +1 -1
- package/dist/{chunk-H35BDAUS.js → chunk-6BGUAOYP.js} +8 -8
- package/dist/{chunk-GDAXWEIJ.js → chunk-6K2QE2UI.js} +731 -424
- package/dist/{chunk-DLLW5ITL.js → chunk-7GADTCNS.js} +30 -9
- package/dist/{chunk-TC4ZEM2D.js → chunk-7SM3P4XR.js} +28 -1
- package/dist/{chunk-WKU7TY22.js → chunk-FIANEE7Z.js} +3 -1
- package/dist/{chunk-RXRQJF7Y.js → chunk-GCGJGSQL.js} +21 -0
- package/dist/{chunk-ZYHLNTC2.js → chunk-GT2ZUALJ.js} +584 -25
- package/dist/{chunk-KNCELO2M.js → chunk-HN6LFYG3.js} +1 -1
- package/dist/{chunk-N3C4XCRE.js → chunk-HWUBBHLK.js} +1 -1
- package/dist/{chunk-BFYVIOTS.js → chunk-KC5EE27Y.js} +13 -3
- package/dist/chunk-L72GDKSN.js +37 -0
- package/dist/{chunk-F3SWGSPG.js → chunk-MLZFK4KP.js} +1 -1
- package/dist/{chunk-RYFAVWP3.js → chunk-PJPKHGLG.js} +34 -15
- package/dist/{chunk-CY7NXM32.js → chunk-PWRFTDTY.js} +4 -2
- package/dist/{chunk-WLT72MRP.js → chunk-QWCF4RAC.js} +1 -1
- package/dist/{chunk-CGXLCMWI.js → chunk-S6R23MFB.js} +117 -17
- package/dist/{chunk-OY2PQQ4P.js → chunk-UXLYAVYL.js} +1 -1
- package/dist/cli/chat.js +23 -24
- package/dist/cli/check.js +6 -6
- package/dist/cli/main.js +1 -1
- package/dist/cli/plugins.js +12 -14
- package/dist/cli/{snapshot-warmup.js → snapshot-create.js} +17 -20
- package/dist/cli/upgrade.js +7 -7
- package/dist/db/schema/stats.d.ts +93 -0
- package/dist/db/schema.d.ts +94 -1
- package/dist/{db-FH4R5ZQ4.js → db-PMRHFSYL.js} +6 -6
- package/dist/handlers/oauth-html.d.ts +4 -2
- package/dist/instrumentation.js +2 -2
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-C2SSJ3FQ.js → oauth-callback-server-QJUC7S4X.js} +15 -17
- package/dist/{plugins-IUJA4VBU.js → plugins-HW4VWW6G.js} +1 -1
- package/dist/{runner-NAALFIXM.js → runner-PZMV6JQW.js} +10 -12
- package/dist/{skills-UN2JXYYF.js → skills-W4JETYSV.js} +6 -6
- package/dist/stats.d.ts +18 -0
- package/dist/{validation-EEF4ROHR.js → validation-IJLNHZSU.js} +6 -6
- package/dist/version.js +1 -1
- package/migrations/0011_sour_golden_guardian.sql +8 -0
- package/migrations/meta/0011_snapshot.json +1195 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +5 -5
- package/dist/chat/sandbox/runtime-dependency-snapshots.d.ts +0 -20
- package/dist/chunk-4ZNGQH7C.js +0 -25
- package/dist/chunk-FAEALXEH.js +0 -517
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type ResolveOutcome = "no_profile" | "cache_hit" | "cache_hit_after_lock_wait" | "rebuilt" | "forced_rebuild";
|
|
2
|
+
export type RebuildReason = "cache_miss" | "floating_stale" | "force_rebuild" | "snapshot_missing";
|
|
3
|
+
/** Result of resolving the reusable snapshot for one dependency profile. */
|
|
4
|
+
export interface Snapshot {
|
|
5
|
+
snapshotId?: string;
|
|
6
|
+
profileHash?: string;
|
|
7
|
+
dependencyCount: number;
|
|
8
|
+
cacheHit: boolean;
|
|
9
|
+
resolveOutcome: ResolveOutcome;
|
|
10
|
+
rebuildReason?: RebuildReason;
|
|
11
|
+
}
|
|
12
|
+
export type ProgressPhase = "resolve_start" | "cache_hit" | "waiting_for_lock" | "building_snapshot" | "build_complete";
|
|
13
|
+
/** Resolve or build the reusable snapshot for the current dependency profile. */
|
|
14
|
+
export declare function resolve(params: {
|
|
15
|
+
runtime: string;
|
|
16
|
+
timeoutMs: number;
|
|
17
|
+
forceRebuild?: boolean;
|
|
18
|
+
staleSnapshotId?: string;
|
|
19
|
+
onProgress?: (phase: ProgressPhase) => void | Promise<void>;
|
|
20
|
+
}): Promise<Snapshot>;
|
|
21
|
+
/** Identify provider errors that mean a referenced snapshot no longer exists. */
|
|
22
|
+
export declare function isMissingError(error: unknown): boolean;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { PiMessage } from "@/chat/pi/messages";
|
|
2
2
|
export declare const COMPACTION_SUMMARY_PREFIX = "Context compaction summary for future Junior turns:";
|
|
3
|
+
export declare const ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX = "Active-turn context checkpoint. If no newer user instruction follows this checkpoint, continue the same unfinished task now using the authoritative instructions above and this summary as internal continuation state. If a newer user instruction follows, treat this checkpoint as prior context and follow that newer instruction. Do not reply with a plan or summary solely because this checkpoint appeared:";
|
|
3
4
|
export declare const MODEL_HANDOFF_SUMMARY_PREFIX = "Model handoff checkpoint. Continue the outstanding request now using this summary as the complete prior context:";
|
|
4
5
|
/** Return whether text is one of Junior's durable compacted-context markers. */
|
|
5
6
|
export declare function isCompactionSummaryText(text: string): boolean;
|
|
@@ -4,6 +4,7 @@ import type { Destination, Actor, Source } from "@sentry/junior-plugin-api";
|
|
|
4
4
|
import type { PiMessage } from "@/chat/pi/messages";
|
|
5
5
|
import type { ConversationMessageProvenance } from "@/chat/conversations/provenance";
|
|
6
6
|
import { type AgentTurnUsage } from "@/chat/usage";
|
|
7
|
+
import type { PluginTurnContext } from "@/chat/plugins/prompt";
|
|
7
8
|
export interface TurnSessionContext {
|
|
8
9
|
conversationId: string;
|
|
9
10
|
sessionId: string;
|
|
@@ -40,6 +41,7 @@ export declare function persistRunningSessionRecord(args: {
|
|
|
40
41
|
actor?: Actor;
|
|
41
42
|
reasoningLevel?: string;
|
|
42
43
|
surface?: AgentTurnSurface;
|
|
44
|
+
turnContexts?: PluginTurnContext[];
|
|
43
45
|
turnStartMessageIndex?: number;
|
|
44
46
|
}): Promise<boolean>;
|
|
45
47
|
/**
|
package/dist/chat/skills.d.ts
CHANGED
|
@@ -31,7 +31,6 @@ export interface Skill extends SkillMetadata {
|
|
|
31
31
|
}
|
|
32
32
|
export interface SkillInvocation {
|
|
33
33
|
skillName: string;
|
|
34
|
-
args: string;
|
|
35
34
|
}
|
|
36
35
|
export interface DiscoverSkillsOptions {
|
|
37
36
|
additionalRoots?: string[];
|
|
@@ -40,7 +39,7 @@ export interface DiscoverSkillsOptions {
|
|
|
40
39
|
export declare function resetSkillDiscoveryCache(): void;
|
|
41
40
|
/** Scan all configured skill roots and return discovered skill metadata, using a cache when roots match. */
|
|
42
41
|
export declare function discoverSkills(options?: DiscoverSkillsOptions): Promise<SkillMetadata[]>;
|
|
43
|
-
/** Extract
|
|
42
|
+
/** Extract an explicit skill invocation from a user message. */
|
|
44
43
|
export declare function parseSkillInvocation(messageText: string, availableSkills: SkillMetadata[]): SkillInvocation | null;
|
|
45
44
|
/** Look up a skill by name from the available set. */
|
|
46
45
|
export declare function findSkillByName(skillName: string, available: SkillMetadata[]): SkillMetadata | null;
|
|
@@ -12,6 +12,7 @@ import { type Destination, type Source } from "@sentry/junior-plugin-api";
|
|
|
12
12
|
import { type PiMessage } from "@/chat/pi/messages";
|
|
13
13
|
import { type Actor } from "@/chat/actor";
|
|
14
14
|
import { type ConversationMessageProvenance } from "@/chat/conversations/provenance";
|
|
15
|
+
import type { PluginTurnContext } from "@/chat/plugins/prompt";
|
|
15
16
|
import { type AgentTurnUsage } from "@/chat/usage";
|
|
16
17
|
import type { ConversationPrivacy } from "@/chat/conversation-privacy";
|
|
17
18
|
import type { ConversationStore } from "@/chat/conversations/store";
|
|
@@ -95,6 +96,7 @@ export declare function upsertAgentTurnSessionRecord(args: {
|
|
|
95
96
|
errorMessage?: string;
|
|
96
97
|
resumedFromSliceId?: number;
|
|
97
98
|
traceId?: string;
|
|
99
|
+
turnContexts?: PluginTurnContext[];
|
|
98
100
|
turnStartMessageIndex?: number;
|
|
99
101
|
ttlMs?: number;
|
|
100
102
|
}): Promise<AgentTurnSessionRecord>;
|
|
@@ -2,5 +2,8 @@ import type { SkillMetadata } from "@/chat/skills";
|
|
|
2
2
|
import type { ToolRegistry } from "@/chat/tools/definition";
|
|
3
3
|
import type { ToolHooks, ToolRuntimeContext } from "@/chat/tools/types";
|
|
4
4
|
export type { ToolHooks, ToolRuntimeContext };
|
|
5
|
+
export interface CreateToolsOptions {
|
|
6
|
+
includeLoadSkill?: boolean;
|
|
7
|
+
}
|
|
5
8
|
/** Build the model-facing tool registry from runtime-owned context and capabilities. */
|
|
6
|
-
export declare function createTools(availableSkills: SkillMetadata[], hooks: ToolHooks | undefined, context: ToolRuntimeContext): ToolRegistry;
|
|
9
|
+
export declare function createTools(availableSkills: SkillMetadata[], hooks: ToolHooks | undefined, context: ToolRuntimeContext, options?: CreateToolsOptions): ToolRegistry;
|
|
@@ -9,7 +9,7 @@ export declare function createQueryConversationEventsTool(context: ToolRuntimeCo
|
|
|
9
9
|
after_seq?: number | null | undefined;
|
|
10
10
|
before_seq?: number | null | undefined;
|
|
11
11
|
limit?: number | null | undefined;
|
|
12
|
-
types?: ("message" | "handoff" | "user_message" | "assistant_message" | "tool_result" | "compaction" | "mcp_provider_connected" | "authorization_requested" | "authorization_completed" | "tool_execution_started" | "message_updated" | "message_handled" | "messages_summarized" | "turn_started" | "turn_routed" | "turn_completed" | "turn_failed" | "subagent_started" | "subagent_ended")[] | null | undefined;
|
|
12
|
+
types?: ("message" | "handoff" | "user_message" | "assistant_message" | "tool_result" | "compaction" | "mcp_provider_connected" | "authorization_requested" | "authorization_completed" | "tool_execution_started" | "message_updated" | "message_handled" | "messages_summarized" | "turn_started" | "turn_routed" | "turn_context" | "turn_completed" | "turn_failed" | "subagent_started" | "subagent_ended")[] | null | undefined;
|
|
13
13
|
};
|
|
14
14
|
execute?: ((input: unknown, options: import("./definition").ToolExecuteOptions) => {
|
|
15
15
|
[x: string]: unknown;
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import type { SandboxCommandInput, SandboxCommandResult, SandboxFileSystem } from "@/chat/sandbox/workspace";
|
|
2
2
|
export type { SandboxFileSystem };
|
|
3
3
|
export type SandboxCommandRunner = (input: SandboxCommandInput) => Promise<SandboxCommandResult>;
|
|
4
|
+
/** Safe measurements produced by one sandbox-backed search operation. */
|
|
5
|
+
export interface SandboxSearchTelemetry {
|
|
6
|
+
emittedLineCount: number;
|
|
7
|
+
limit: number;
|
|
8
|
+
limitReached: boolean;
|
|
9
|
+
parsedRecordCount: number;
|
|
10
|
+
rawOutputBytes: number;
|
|
11
|
+
resultCount: number;
|
|
12
|
+
resultBytes: number;
|
|
13
|
+
}
|
|
4
14
|
export declare const MAX_TEXT_CHARS = 60000;
|
|
5
15
|
export declare const RIPGREP_EXCLUDED_GLOBS: readonly ["!**/.git/**", "!**/node_modules/**"];
|
|
16
|
+
export declare const DEFAULT_SEARCH_COMMAND_TIMEOUT_MS = 30000;
|
|
6
17
|
export type TextSearchResultDetails = {
|
|
7
18
|
ok: true;
|
|
8
19
|
status?: "success";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SandboxCommandRunner, type SandboxFileSystem, type TextSearchResultDetails, type TextSearchToolResult } from "@/chat/tools/sandbox/file-utils";
|
|
1
|
+
import { type SandboxCommandRunner, type SandboxFileSystem, type SandboxSearchTelemetry, type TextSearchResultDetails, type TextSearchToolResult } from "@/chat/tools/sandbox/file-utils";
|
|
2
2
|
import { type JuniorToolResultEnvelope } from "@/chat/tool-support/structured-result";
|
|
3
3
|
interface FindFilesSuccessResult {
|
|
4
4
|
content: JuniorToolResultEnvelope["content"];
|
|
@@ -17,6 +17,7 @@ type FindFilesResult = FindFilesSuccessResult | TextSearchToolResult;
|
|
|
17
17
|
export declare function findFiles(params: {
|
|
18
18
|
fs: SandboxFileSystem;
|
|
19
19
|
limit?: unknown;
|
|
20
|
+
onTelemetry?: (telemetry: SandboxSearchTelemetry) => void;
|
|
20
21
|
path?: string;
|
|
21
22
|
pattern: string;
|
|
22
23
|
runCommand?: SandboxCommandRunner;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SandboxCommandRunner, type SandboxFileSystem, type TextSearchResultDetails, type TextSearchToolResult } from "@/chat/tools/sandbox/file-utils";
|
|
1
|
+
import { type SandboxCommandRunner, type SandboxFileSystem, type SandboxSearchTelemetry, type TextSearchResultDetails, type TextSearchToolResult } from "@/chat/tools/sandbox/file-utils";
|
|
2
2
|
interface GrepResultData {
|
|
3
3
|
context: number;
|
|
4
4
|
glob?: string;
|
|
@@ -31,6 +31,7 @@ export declare function grepFiles(params: {
|
|
|
31
31
|
ignoreCase?: boolean;
|
|
32
32
|
limit?: unknown;
|
|
33
33
|
literal?: boolean;
|
|
34
|
+
onTelemetry?: (telemetry: SandboxSearchTelemetry) => void;
|
|
34
35
|
path?: string;
|
|
35
36
|
pattern: string;
|
|
36
37
|
runCommand?: SandboxCommandRunner;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SandboxWorkspace } from "@/chat/sandbox/workspace";
|
|
2
|
+
import type { ViewImageToolDeps } from "@/chat/tools/types";
|
|
3
|
+
/** Create a tool that loads a sandbox image into the active model context. */
|
|
4
|
+
export declare function createViewImageTool(sandbox: SandboxWorkspace, supportsImageInput: () => boolean, deps?: ViewImageToolDeps): Omit<import("../definition").AnyToolDefinition, "inputSchema" | "outputSchema" | "prepareArguments" | "execute"> & {
|
|
5
|
+
inputSchema: import("../definition").JsonSchemaObject;
|
|
6
|
+
outputSchema?: undefined;
|
|
7
|
+
prepareArguments(args: unknown): {
|
|
8
|
+
path: string;
|
|
9
|
+
};
|
|
10
|
+
execute?: (input: unknown, options: import("../definition").ToolExecuteOptions) => Promise<import("@/chat/tool-support/zod-tool").ContentOnlyToolResult> | import("@/chat/tool-support/zod-tool").ContentOnlyToolResult;
|
|
11
|
+
};
|
|
@@ -34,6 +34,10 @@ export interface WebSearchToolDeps {
|
|
|
34
34
|
max_results?: number;
|
|
35
35
|
}) => Promise<JuniorToolResult> | JuniorToolResult;
|
|
36
36
|
}
|
|
37
|
+
/** Optional host image reader for deterministic tool execution without sandbox I/O. */
|
|
38
|
+
export interface ViewImageToolDeps {
|
|
39
|
+
readFile?: (path: string) => Promise<Buffer | null | undefined>;
|
|
40
|
+
}
|
|
37
41
|
export interface ToolHooks {
|
|
38
42
|
/**
|
|
39
43
|
* Materialize generated files and return sandbox paths that exist before the
|
|
@@ -44,6 +48,7 @@ export interface ToolHooks {
|
|
|
44
48
|
onSkillLoaded?: (skill: Skill) => void | LoadSkillMetadata | Promise<void | LoadSkillMetadata>;
|
|
45
49
|
toolOverrides?: {
|
|
46
50
|
imageGenerate?: ImageGenerateToolDeps;
|
|
51
|
+
viewImage?: ViewImageToolDeps;
|
|
47
52
|
webFetch?: WebFetchToolDeps;
|
|
48
53
|
webSearch?: WebSearchToolDeps;
|
|
49
54
|
};
|
|
@@ -70,6 +75,8 @@ interface BaseToolRuntimeContext {
|
|
|
70
75
|
egress: PluginEgress;
|
|
71
76
|
mcpToolManager?: McpToolManager;
|
|
72
77
|
workspace: SandboxWorkspace;
|
|
78
|
+
/** Report whether the model currently executing the turn accepts images. */
|
|
79
|
+
supportsImageInput?: () => boolean;
|
|
73
80
|
}
|
|
74
81
|
interface SlackToolRuntimeContext extends BaseToolRuntimeContext {
|
|
75
82
|
destination: SlackDestination;
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
logInfo,
|
|
6
6
|
logWarn,
|
|
7
7
|
setSpanAttributes
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-MLZFK4KP.js";
|
|
9
9
|
import {
|
|
10
10
|
discoverInstalledPluginPackageContent,
|
|
11
11
|
normalizePluginPackageNames
|
|
@@ -1097,6 +1097,115 @@ function hasRequiredOAuthScope(storedScope, requiredScope) {
|
|
|
1097
1097
|
return required.every((scope) => stored.has(scope));
|
|
1098
1098
|
}
|
|
1099
1099
|
|
|
1100
|
+
// src/chat/oauth-response.ts
|
|
1101
|
+
var MAX_OAUTH_ERROR_BODY_BYTES = 16 * 1024;
|
|
1102
|
+
var MAX_OAUTH_ERROR_BODY_READS = MAX_OAUTH_ERROR_BODY_BYTES;
|
|
1103
|
+
var MAX_OAUTH_ERROR_BODY_READ_MS = 5e3;
|
|
1104
|
+
var OAUTH_ERROR_BODY_READ_TIMEOUT = /* @__PURE__ */ Symbol("oauth-error-body-read-timeout");
|
|
1105
|
+
async function readOAuthErrorBodyChunk(reader) {
|
|
1106
|
+
let timeout;
|
|
1107
|
+
const timeoutResult = new Promise(
|
|
1108
|
+
(resolve) => {
|
|
1109
|
+
timeout = setTimeout(
|
|
1110
|
+
resolve,
|
|
1111
|
+
MAX_OAUTH_ERROR_BODY_READ_MS,
|
|
1112
|
+
OAUTH_ERROR_BODY_READ_TIMEOUT
|
|
1113
|
+
);
|
|
1114
|
+
}
|
|
1115
|
+
);
|
|
1116
|
+
try {
|
|
1117
|
+
const result = await Promise.race([reader.read(), timeoutResult]);
|
|
1118
|
+
if (result === OAUTH_ERROR_BODY_READ_TIMEOUT) {
|
|
1119
|
+
await reader.cancel();
|
|
1120
|
+
throw new Error("OAuth error body read timed out");
|
|
1121
|
+
}
|
|
1122
|
+
return result;
|
|
1123
|
+
} finally {
|
|
1124
|
+
if (timeout) {
|
|
1125
|
+
clearTimeout(timeout);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
var OAuthProviderError = class extends Error {
|
|
1130
|
+
phase;
|
|
1131
|
+
provider;
|
|
1132
|
+
resourceHost;
|
|
1133
|
+
status;
|
|
1134
|
+
constructor(details) {
|
|
1135
|
+
super(`OAuth provider ${details.phase.replace("_", " ")} failed`);
|
|
1136
|
+
this.name = "OAuthProviderError";
|
|
1137
|
+
this.phase = details.phase;
|
|
1138
|
+
this.provider = details.provider;
|
|
1139
|
+
this.resourceHost = details.resourceHost;
|
|
1140
|
+
this.status = details.status;
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
function getOAuthProviderErrorAttributes(error) {
|
|
1144
|
+
if (!(error instanceof OAuthProviderError)) {
|
|
1145
|
+
return {};
|
|
1146
|
+
}
|
|
1147
|
+
return {
|
|
1148
|
+
"app.credential.provider": error.provider,
|
|
1149
|
+
"app.oauth.error.phase": error.phase,
|
|
1150
|
+
"server.address": error.resourceHost,
|
|
1151
|
+
...error.status !== void 0 ? { "http.response.status_code": error.status } : {}
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
async function readBoundedOAuthErrorBody(response) {
|
|
1155
|
+
if (!response.body) {
|
|
1156
|
+
return "";
|
|
1157
|
+
}
|
|
1158
|
+
const reader = response.body.getReader();
|
|
1159
|
+
const decoder = new TextDecoder();
|
|
1160
|
+
let bytesRead = 0;
|
|
1161
|
+
let reads = 0;
|
|
1162
|
+
let text = "";
|
|
1163
|
+
try {
|
|
1164
|
+
while (bytesRead < MAX_OAUTH_ERROR_BODY_BYTES && reads < MAX_OAUTH_ERROR_BODY_READS) {
|
|
1165
|
+
reads += 1;
|
|
1166
|
+
const { done, value } = await readOAuthErrorBodyChunk(reader);
|
|
1167
|
+
if (done) {
|
|
1168
|
+
return text + decoder.decode();
|
|
1169
|
+
}
|
|
1170
|
+
const remaining = MAX_OAUTH_ERROR_BODY_BYTES - bytesRead;
|
|
1171
|
+
const chunk = value.subarray(0, remaining);
|
|
1172
|
+
bytesRead += chunk.byteLength;
|
|
1173
|
+
text += decoder.decode(chunk, { stream: true });
|
|
1174
|
+
if (chunk.byteLength < value.byteLength) {
|
|
1175
|
+
await reader.cancel();
|
|
1176
|
+
return text + decoder.decode();
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
await reader.cancel();
|
|
1180
|
+
return text + decoder.decode();
|
|
1181
|
+
} finally {
|
|
1182
|
+
reader.releaseLock();
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
function fetchWithBoundedOAuthErrorBodies(fetchFn = globalThis.fetch, onErrorResponseStatus) {
|
|
1186
|
+
return (async (input, init) => {
|
|
1187
|
+
const response = await fetchFn(input, init);
|
|
1188
|
+
if (response.ok) {
|
|
1189
|
+
return response;
|
|
1190
|
+
}
|
|
1191
|
+
onErrorResponseStatus?.(response.status);
|
|
1192
|
+
if (!response.body) {
|
|
1193
|
+
return response;
|
|
1194
|
+
}
|
|
1195
|
+
let body = "";
|
|
1196
|
+
try {
|
|
1197
|
+
body = await readBoundedOAuthErrorBody(response);
|
|
1198
|
+
} catch {
|
|
1199
|
+
await response.body.cancel().catch(() => void 0);
|
|
1200
|
+
}
|
|
1201
|
+
return new Response(body, {
|
|
1202
|
+
headers: response.headers,
|
|
1203
|
+
status: response.status,
|
|
1204
|
+
statusText: response.statusText
|
|
1205
|
+
});
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1100
1209
|
// src/chat/plugins/command-env.ts
|
|
1101
1210
|
var ENV_PLACEHOLDER_RE2 = /\$\{([A-Z_][A-Z0-9_]*)\}/g;
|
|
1102
1211
|
function resolveValue(manifest, value) {
|
|
@@ -1320,7 +1429,7 @@ function parseRefreshError(text) {
|
|
|
1320
1429
|
return void 0;
|
|
1321
1430
|
}
|
|
1322
1431
|
}
|
|
1323
|
-
async function refreshAccessToken(refreshToken, oauth, requestedScope) {
|
|
1432
|
+
async function refreshAccessToken(provider, refreshToken, oauth, requestedScope) {
|
|
1324
1433
|
const clientId = process.env[oauth.clientIdEnv]?.trim();
|
|
1325
1434
|
const clientSecret = process.env[oauth.clientSecretEnv]?.trim();
|
|
1326
1435
|
if (!clientId || !clientSecret) {
|
|
@@ -1338,28 +1447,49 @@ async function refreshAccessToken(refreshToken, oauth, requestedScope) {
|
|
|
1338
1447
|
tokenAuthMethod: oauth.tokenAuthMethod,
|
|
1339
1448
|
tokenExtraHeaders: oauth.tokenExtraHeaders
|
|
1340
1449
|
});
|
|
1341
|
-
const
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1450
|
+
const errorDetails = {
|
|
1451
|
+
phase: "token_refresh",
|
|
1452
|
+
provider,
|
|
1453
|
+
resourceHost: new URL(oauth.tokenEndpoint).hostname
|
|
1454
|
+
};
|
|
1455
|
+
let response;
|
|
1456
|
+
try {
|
|
1457
|
+
response = await fetch(oauth.tokenEndpoint, {
|
|
1458
|
+
method: "POST",
|
|
1459
|
+
headers: request.headers,
|
|
1460
|
+
body: request.body,
|
|
1461
|
+
signal: AbortSignal.timeout(TOKEN_REFRESH_TIMEOUT_MS)
|
|
1462
|
+
});
|
|
1463
|
+
} catch {
|
|
1464
|
+
throw new OAuthProviderError(errorDetails);
|
|
1465
|
+
}
|
|
1347
1466
|
if (!response.ok) {
|
|
1348
|
-
|
|
1467
|
+
let errorCode;
|
|
1468
|
+
try {
|
|
1469
|
+
errorCode = parseRefreshError(await readBoundedOAuthErrorBody(response));
|
|
1470
|
+
} catch {
|
|
1471
|
+
throw new OAuthProviderError({
|
|
1472
|
+
...errorDetails,
|
|
1473
|
+
status: response.status
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1349
1476
|
if (errorCode === "invalid_grant" || errorCode === "bad_refresh_token") {
|
|
1350
|
-
throw new OAuthRefreshRejectedError(
|
|
1351
|
-
`Token refresh rejected: ${errorCode}`
|
|
1352
|
-
);
|
|
1477
|
+
throw new OAuthRefreshRejectedError("Token refresh was rejected");
|
|
1353
1478
|
}
|
|
1354
|
-
throw new
|
|
1355
|
-
|
|
1356
|
-
|
|
1479
|
+
throw new OAuthProviderError({
|
|
1480
|
+
...errorDetails,
|
|
1481
|
+
status: response.status
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
try {
|
|
1485
|
+
const data = await response.json();
|
|
1486
|
+
return parseOAuthTokenResponse(data, requestedScope, {
|
|
1487
|
+
treatEmptyScopeAsUnreported: oauth.treatEmptyScopeAsUnreported,
|
|
1488
|
+
refreshToken
|
|
1489
|
+
});
|
|
1490
|
+
} catch {
|
|
1491
|
+
throw new OAuthProviderError(errorDetails);
|
|
1357
1492
|
}
|
|
1358
|
-
const data = await response.json();
|
|
1359
|
-
return parseOAuthTokenResponse(data, requestedScope, {
|
|
1360
|
-
treatEmptyScopeAsUnreported: oauth.treatEmptyScopeAsUnreported,
|
|
1361
|
-
refreshToken
|
|
1362
|
-
});
|
|
1363
1493
|
}
|
|
1364
1494
|
function getLeaseExpiry(expiresAt) {
|
|
1365
1495
|
return expiresAt ? Math.min(expiresAt, Date.now() + MAX_LEASE_MS2) : Date.now() + MAX_LEASE_MS2;
|
|
@@ -1447,6 +1577,7 @@ function createOAuthBearerBroker(manifest, credentials, deps) {
|
|
|
1447
1577
|
);
|
|
1448
1578
|
}
|
|
1449
1579
|
const refreshed = await refreshAccessToken(
|
|
1580
|
+
provider,
|
|
1450
1581
|
latest.refreshToken,
|
|
1451
1582
|
oauth,
|
|
1452
1583
|
latest.scope ?? oauth.scope
|
|
@@ -1955,6 +2086,9 @@ export {
|
|
|
1955
2086
|
parseInlinePluginManifest,
|
|
1956
2087
|
CredentialUnavailableError,
|
|
1957
2088
|
hasRequiredOAuthScope,
|
|
2089
|
+
OAuthProviderError,
|
|
2090
|
+
getOAuthProviderErrorAttributes,
|
|
2091
|
+
fetchWithBoundedOAuthErrorBodies,
|
|
1958
2092
|
resolvePluginCommandEnv,
|
|
1959
2093
|
resolveAuthTokenPlaceholder,
|
|
1960
2094
|
buildOAuthTokenRequest,
|