@sentry/junior 0.136.1 → 0.138.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-2KDSPPO4.js → agent-hooks-SCR74Q3E.js} +11 -9
- package/dist/api/conversations/projection.d.ts +1 -0
- package/dist/api/conversations/stats.d.ts +1 -0
- package/dist/api/schema/conversation.d.ts +2 -0
- package/dist/api/schema.js +1 -1
- package/dist/api.js +187 -46
- package/dist/app.d.ts +8 -1
- package/dist/app.js +519 -860
- package/dist/{catalog-runtime-WSZQGC4D.js → catalog-runtime-IU3QF4GG.js} +5 -5
- package/dist/chat/agent/request.d.ts +32 -2
- package/dist/chat/agent-dispatch/heartbeat.d.ts +5 -0
- package/dist/chat/agent-dispatch/types.d.ts +1 -1
- package/dist/chat/agent-invocations/errors.d.ts +8 -0
- package/dist/chat/agent-invocations/spawn.d.ts +12 -0
- package/dist/chat/agent-invocations/store.d.ts +44 -0
- package/dist/chat/agent-invocations/types.d.ts +449 -0
- package/dist/chat/agent-invocations/work.d.ts +31 -0
- package/dist/chat/config.d.ts +1 -0
- package/dist/chat/configuration/defaults.d.ts +1 -1
- package/dist/chat/conversations/sql/store.d.ts +8 -0
- package/dist/chat/conversations/store.d.ts +9 -1
- package/dist/chat/experimental.d.ts +16 -0
- package/dist/chat/pi/client.d.ts +1 -8
- package/dist/chat/pi/gateway-auth.d.ts +16 -0
- package/dist/chat/runtime/agent-runner.d.ts +2 -1
- package/dist/chat/sandbox/docker.d.ts +17 -0
- package/dist/chat/services/agent-continue.d.ts +2 -0
- package/dist/chat/services/auth-pause.d.ts +0 -1
- package/dist/chat/services/mcp-auth-orchestration.d.ts +3 -2
- package/dist/chat/services/plugin-auth-orchestration.d.ts +3 -2
- package/dist/chat/services/subscribed-reply-router-policy.d.ts +12 -0
- package/dist/chat/services/turn-session-routing.d.ts +17 -0
- package/dist/chat/slack/message.d.ts +6 -1
- package/dist/chat/slack/source-link.d.ts +8 -2
- package/dist/chat/slack/team-domain.d.ts +4 -0
- package/dist/chat/source.d.ts +5 -5
- package/dist/chat/state/turn-session.d.ts +0 -2
- package/dist/chat/task-execution/state.d.ts +23 -11
- package/dist/chat/task-execution/store.d.ts +20 -11
- package/dist/chat/task-execution/worker.d.ts +2 -2
- package/dist/chat/tools/runtime/spawn-agent.d.ts +31 -0
- package/dist/chat/tools/types.d.ts +2 -0
- package/dist/chunk-3Y47VMHV.js +6 -0
- package/dist/chunk-5IDFPCON.js +46 -0
- package/dist/chunk-5LTJ4ITU.js +1523 -0
- package/dist/{chunk-KLBYCSLJ.js → chunk-5MXWHTCK.js} +17 -434
- package/dist/{chunk-CZZDCH3Q.js → chunk-A7WCHXZV.js} +206 -78
- package/dist/{chunk-4YQ6I6VF.js → chunk-BOLJOV5T.js} +2 -56
- package/dist/{chunk-XCZ6COFO.js → chunk-BYW4B4PR.js} +90 -54
- package/dist/{chunk-JQRTXJHD.js → chunk-C7ZVPO4Z.js} +2 -1
- package/dist/{chunk-MW2QRPGW.js → chunk-DCLLQGS6.js} +119 -50
- package/dist/{chunk-CDVCCOMF.js → chunk-E5UYAIKJ.js} +75 -1475
- package/dist/{chunk-3SUK3UXX.js → chunk-ELKW5FIB.js} +5 -5
- package/dist/chunk-EM225ITJ.js +56 -0
- package/dist/{chunk-Y6JNHTRD.js → chunk-FEUNPTGV.js} +34 -70
- package/dist/{chunk-TYNMSQKA.js → chunk-FNLBTB4K.js} +1 -1
- package/dist/{chunk-TRZSM67C.js → chunk-HCIVH3JS.js} +1 -0
- package/dist/chunk-HIWJWWMT.js +668 -0
- package/dist/chunk-IUFM5JWV.js +24 -0
- package/dist/{chunk-XWPA5KZF.js → chunk-JNNJZURV.js} +1 -1
- package/dist/{chunk-6ZK6FZIV.js → chunk-KCBZSJRU.js} +6 -27
- package/dist/{chunk-BG3KWWDJ.js → chunk-KDCHVT25.js} +1 -1
- package/dist/{chunk-VH6MI4L7.js → chunk-KFRHRSCJ.js} +1 -1
- package/dist/chunk-LPVUNO2W.js +320 -0
- package/dist/{chunk-G66RYKRS.js → chunk-N7WARMC6.js} +16 -776
- package/dist/chunk-PB3CXNVL.js +41 -0
- package/dist/chunk-Q6QZTTLS.js +55 -0
- package/dist/{chunk-3QNJOHKG.js → chunk-QS527AV5.js} +1 -1
- package/dist/chunk-R776HKPK.js +81 -0
- package/dist/{chunk-OTJMQYVP.js → chunk-RZIBSCYC.js} +517 -608
- package/dist/chunk-SD3OFRWY.js +714 -0
- package/dist/{chunk-HIRIWTWD.js → chunk-SIPKWSXK.js} +58 -10
- package/dist/chunk-TCS76X5D.js +441 -0
- package/dist/{chunk-UX2TNH5D.js → chunk-TDJCLNDH.js} +3 -3
- package/dist/{chunk-NVVBEJUB.js → chunk-TUUDXEXU.js} +336 -300
- package/dist/{chunk-22Z6MGMK.js → chunk-VPZFVJND.js} +1 -1
- package/dist/{chunk-RFDD3Z2I.js → chunk-YU5QUF67.js} +1 -1
- package/dist/chunk-ZBSMCXKV.js +710 -0
- package/dist/{chunk-FSYXYBWU.js → chunk-ZXNTJJIE.js} +2 -2
- package/dist/cli/chat.js +65 -26
- package/dist/cli/check.js +7 -7
- package/dist/cli/plugins.js +14 -12
- package/dist/cli/snapshot-create.js +10 -9
- package/dist/cli/upgrade.js +7 -7
- package/dist/conversation-work-KATFHLG3.js +56 -0
- package/dist/db/schema/agent-invocations.d.ts +544 -0
- package/dist/db/schema.d.ts +543 -1
- package/dist/{db-Z2NNL2JC.js → db-UO2IWXGA.js} +7 -7
- package/dist/experimental-HH6OM6UH.js +13 -0
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +4 -3
- package/dist/{oauth-callback-server-PO3M5JMG.js → oauth-callback-server-MAQI43LX.js} +26 -18
- package/dist/{runner-QHQYZYJA.js → runner-PIUQEXKE.js} +26 -14
- package/dist/{skills-KXMT3DRA.js → skills-4ARJADHQ.js} +6 -6
- package/dist/spawn-G2AWPMCT.js +38 -0
- package/dist/{validation-Y42ULTT6.js → validation-S3EE3HWL.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/work-UARRUAW3.js +46 -0
- package/dist/worker-DOCIRVD3.js +24 -0
- package/migrations/0021_first_warhawk.sql +35 -0
- package/migrations/meta/0021_snapshot.json +2176 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +6 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Store install-wide
|
|
1
|
+
/** Store install-wide configuration defaults. */
|
|
2
2
|
export declare function setConfigDefaults(defaults: Record<string, unknown> | undefined): void;
|
|
3
3
|
/** Return the install-wide configuration defaults (empty object when none set). */
|
|
4
4
|
export declare function getConfigDefaults(): Record<string, unknown>;
|
|
@@ -5,11 +5,19 @@ import type { JuniorSqlDatabase } from "@/db/db";
|
|
|
5
5
|
import type { Conversation, ConversationExecution, ConversationSource, ConversationStore } from "../store";
|
|
6
6
|
import type { AgentTurnUsage } from "@/chat/usage";
|
|
7
7
|
import { type ProviderConversationBinding, type ProviderConversationReference } from "./bindings";
|
|
8
|
+
/** Serialize one conversation's durable mutations inside a SQL transaction. */
|
|
9
|
+
export declare function withConversationMutationLock<T>(executor: JuniorSqlDatabase, conversationId: string, callback: () => Promise<T>): Promise<T>;
|
|
8
10
|
export declare class SqlStore implements ConversationStore {
|
|
9
11
|
private readonly executor;
|
|
10
12
|
constructor(executor: JuniorSqlDatabase);
|
|
11
13
|
getConversationIdByProviderConversation(args: ProviderConversationReference): Promise<string | undefined>;
|
|
12
14
|
bindProviderConversation(args: ProviderConversationBinding): Promise<void>;
|
|
15
|
+
createChild(args: {
|
|
16
|
+
childConversationId: string;
|
|
17
|
+
parentConversationId: string;
|
|
18
|
+
nowMs?: number;
|
|
19
|
+
source?: ConversationSource;
|
|
20
|
+
}): Promise<void>;
|
|
13
21
|
get(args: {
|
|
14
22
|
conversationId: string;
|
|
15
23
|
}): Promise<Conversation | undefined>;
|
|
@@ -32,7 +32,8 @@ export interface Conversation {
|
|
|
32
32
|
source?: ConversationSource;
|
|
33
33
|
/**
|
|
34
34
|
* Structured inbound Source locator for this conversation session.
|
|
35
|
-
* Session-stable (Slack
|
|
35
|
+
* Session-stable (threaded Slack keeps threadTs; channel-level turns omit
|
|
36
|
+
* it; never stores per-message ts). Set-once.
|
|
36
37
|
*/
|
|
37
38
|
sessionSource?: SessionSource;
|
|
38
39
|
title?: string;
|
|
@@ -48,6 +49,13 @@ export interface Conversation {
|
|
|
48
49
|
}
|
|
49
50
|
/** Persist and read durable conversation metadata for reporting surfaces. */
|
|
50
51
|
export interface ConversationStore {
|
|
52
|
+
/** Create one destinationless child with immutable parent lineage. */
|
|
53
|
+
createChild(args: {
|
|
54
|
+
childConversationId: string;
|
|
55
|
+
parentConversationId: string;
|
|
56
|
+
nowMs?: number;
|
|
57
|
+
source?: ConversationSource;
|
|
58
|
+
}): Promise<void>;
|
|
51
59
|
get(args: {
|
|
52
60
|
conversationId: string;
|
|
53
61
|
}): Promise<Conversation | undefined>;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Known unstable product features that must be opted into explicitly.
|
|
3
|
+
* Add new keys here as features graduate from private experiments; remove them
|
|
4
|
+
* once they become stable defaults.
|
|
5
|
+
*/
|
|
6
|
+
export declare const EXPERIMENTAL_FEATURES: readonly ["subagents"];
|
|
7
|
+
/** One known experimental feature name. */
|
|
8
|
+
export type ExperimentalFeature = (typeof EXPERIMENTAL_FEATURES)[number];
|
|
9
|
+
/** App-level opt-ins for unstable product features. */
|
|
10
|
+
export type ExperimentalFeaturesConfig = Readonly<Partial<Record<ExperimentalFeature, boolean>>>;
|
|
11
|
+
/** Replace app-level experimental opt-ins and return the previous setting. */
|
|
12
|
+
export declare function setExperimentalFeatures(config?: ExperimentalFeaturesConfig): ExperimentalFeaturesConfig;
|
|
13
|
+
/** Return a copy of the app-level experimental opt-ins configured by createApp(). */
|
|
14
|
+
export declare function getExperimentalFeatures(): ExperimentalFeaturesConfig;
|
|
15
|
+
/** Return whether one experimental feature is enabled via createApp({ experimental }). */
|
|
16
|
+
export declare function isExperimentalFeatureEnabled(feature: ExperimentalFeature): boolean;
|
package/dist/chat/pi/client.d.ts
CHANGED
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import { type Message, type Model, type ThinkingLevel } from "@/chat/pi/sdk";
|
|
2
|
+
export { getGatewayApiKey, MISSING_GATEWAY_CREDENTIALS_ERROR, resolveGatewayCredential, type GatewayCredential, } from "@/chat/pi/gateway-auth";
|
|
2
3
|
import type { ZodTypeAny, z } from "zod";
|
|
3
4
|
export declare const GEN_AI_PROVIDER_NAME: "vercel-ai-gateway";
|
|
4
5
|
export declare const GEN_AI_SERVER_ADDRESS = "ai-gateway.vercel.sh";
|
|
5
6
|
export declare const GEN_AI_SERVER_PORT = 443;
|
|
6
|
-
export declare const MISSING_GATEWAY_CREDENTIALS_ERROR = "Missing AI gateway credentials (AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN)";
|
|
7
|
-
/**
|
|
8
|
-
* Resolve the documented AI Gateway env credentials for the paths that need
|
|
9
|
-
* the bearer token string directly.
|
|
10
|
-
*/
|
|
11
|
-
export declare function getGatewayApiKey(): string | undefined;
|
|
12
|
-
/** Return the Gateway credential shape expected by Pi Agent getApiKey hooks. */
|
|
13
|
-
export declare function getPiGatewayApiKey(): string | undefined;
|
|
14
7
|
/**
|
|
15
8
|
* Look up a gateway model by id. Throws `Unknown AI Gateway model id: …` if
|
|
16
9
|
* the id is not in pi-ai's registry — callers at the config boundary can use
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type GatewayCredential = {
|
|
2
|
+
mode: "oidc" | "api_key";
|
|
3
|
+
token: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const MISSING_GATEWAY_CREDENTIALS_ERROR = "Missing AI gateway credentials (enable Vercel OIDC or set AI_GATEWAY_API_KEY)";
|
|
6
|
+
/**
|
|
7
|
+
* Resolve the preferred AI Gateway bearer credential.
|
|
8
|
+
*
|
|
9
|
+
* Order:
|
|
10
|
+
* 1. Vercel OIDC via `@vercel/oidc` (request header `x-vercel-oidc-token`,
|
|
11
|
+
* then `VERCEL_OIDC_TOKEN`, with local refresh when needed)
|
|
12
|
+
* 2. Explicit `AI_GATEWAY_API_KEY` / pi-ai env mapping for vercel-ai-gateway
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveGatewayCredential(): Promise<GatewayCredential | undefined>;
|
|
15
|
+
/** Resolve the bearer token string for Authorization headers and Pi getApiKey. */
|
|
16
|
+
export declare function getGatewayApiKey(): Promise<string | undefined>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type AgentRunRequest, type SpawnAgent } from "@/chat/agent/request";
|
|
2
2
|
import type { AgentRunOutcome } from "@/chat/runtime/agent-run-outcome";
|
|
3
3
|
import type { SandboxEgressTracePropagationConfig } from "@/chat/sandbox/egress/tracing";
|
|
4
4
|
/** Run one agent-run slice behind runtime-owned orchestration boundaries. */
|
|
@@ -7,5 +7,6 @@ export interface AgentRunner {
|
|
|
7
7
|
}
|
|
8
8
|
/** Adapt the Pi-facing agent-run executor behind the runtime-owned runner seam. */
|
|
9
9
|
export declare function createAgentRunner(run: AgentRunner["run"], options?: {
|
|
10
|
+
bindSpawnAgent?: (request: AgentRunRequest) => SpawnAgent | undefined;
|
|
10
11
|
tracePropagation?: SandboxEgressTracePropagationConfig;
|
|
11
12
|
}): AgentRunner;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SandboxWorkspace } from "@/chat/sandbox/workspace";
|
|
2
|
+
/** Docker daemon config for Vercel sandbox guests (nested containers). */
|
|
3
|
+
export declare const DOCKER_DAEMON_JSON = "{\n \"storage-driver\": \"overlay2\",\n \"live-restore\": false\n}\n";
|
|
4
|
+
/**
|
|
5
|
+
* Start dockerd when the socket is missing and wait until `docker info` works.
|
|
6
|
+
* Re-execs through sudo so the sandbox user can call it without root.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DOCKER_ENSURE_SCRIPT = "#!/bin/sh\nset -eu\n\nif [ \"$(id -u)\" -ne 0 ]; then\n exec sudo -n \"$0\" \"$@\"\nfi\n\nSOCK=/var/run/docker.sock\nPIDFILE=/var/run/docker.pid\nLOG=/var/log/junior-dockerd.log\n\nis_ready() {\n [ -S \"$SOCK\" ] && /usr/bin/docker info >/dev/null 2>&1\n}\n\nif is_ready; then\n chmod 666 \"$SOCK\" 2>/dev/null || true\n exit 0\nfi\n\nmkdir -p /etc/docker /var/lib/docker /var/run /var/log\nif [ ! -f /etc/docker/daemon.json ]; then\n cat > /etc/docker/daemon.json <<'EOF'\n{\n \"storage-driver\": \"overlay2\",\n \"live-restore\": false\n}\nEOF\nfi\n\nif [ -f \"$PIDFILE\" ]; then\n oldpid=\"$(cat \"$PIDFILE\" 2>/dev/null || true)\"\n if [ -n \"${oldpid:-}\" ] && ! kill -0 \"$oldpid\" 2>/dev/null; then\n rm -f \"$PIDFILE\"\n fi\nfi\n\nif [ ! -f \"$PIDFILE\" ] || ! kill -0 \"$(cat \"$PIDFILE\" 2>/dev/null)\" 2>/dev/null; then\n # dockerd must outlive this helper; nohup + background is intentional.\n nohup dockerd --host=\"unix://$SOCK\" --pidfile=\"$PIDFILE\" >\"$LOG\" 2>&1 &\nfi\n\ni=0\nwhile [ \"$i\" -lt 50 ]; do\n if is_ready; then\n chmod 666 \"$SOCK\" 2>/dev/null || true\n exit 0\n fi\n i=$((i + 1))\n sleep 0.1\ndone\n\necho \"junior-ensure-docker: dockerd failed to become ready\" >&2\nif [ -f \"$LOG\" ]; then\n tail -n 50 \"$LOG\" >&2 || true\nfi\nexit 1\n";
|
|
9
|
+
/** PATH wrapper so plain `docker` starts the daemon on first use. */
|
|
10
|
+
export declare const DOCKER_CLI_WRAPPER_SCRIPT = "#!/bin/sh\nset -eu\nif command -v junior-ensure-docker >/dev/null 2>&1; then\n junior-ensure-docker\nfi\nexec /usr/bin/docker \"$@\"\n";
|
|
11
|
+
/** PATH wrapper so plain `docker-compose` starts the daemon on first use. */
|
|
12
|
+
export declare const DOCKER_COMPOSE_CLI_WRAPPER_SCRIPT = "#!/bin/sh\nset -eu\nif command -v junior-ensure-docker >/dev/null 2>&1; then\n junior-ensure-docker\nfi\nexec /usr/local/libexec/junior-docker-compose \"$@\"\n";
|
|
13
|
+
/**
|
|
14
|
+
* Best-effort dockerd start during sandbox prepare.
|
|
15
|
+
* Missing helper is a no-op so tests and base sandboxes stay green.
|
|
16
|
+
*/
|
|
17
|
+
export declare function ensureDockerDaemon(sandbox: Pick<SandboxWorkspace, "runCommand">, signal?: AbortSignal): Promise<void>;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { StateAdapter } from "chat";
|
|
8
8
|
import type { Destination } from "@sentry/junior-plugin-api";
|
|
9
|
+
import type { ConversationStore } from "@/chat/conversations/store";
|
|
9
10
|
import type { ConversationWorkQueue } from "@/chat/task-execution/queue";
|
|
10
11
|
export interface AgentContinueRequest {
|
|
11
12
|
conversationId: string;
|
|
@@ -21,6 +22,7 @@ export interface ScheduleAgentContinueOptions {
|
|
|
21
22
|
/** Build the queue request for an awaiting automatic agent continuation. */
|
|
22
23
|
export declare function getAwaitingAgentContinueRequest(args: {
|
|
23
24
|
conversationId: string;
|
|
25
|
+
conversationStore?: ConversationStore;
|
|
24
26
|
sessionId: string;
|
|
25
27
|
}): Promise<AgentContinueRequest | undefined>;
|
|
26
28
|
/** Schedule durable conversation work to continue a paused agent run. */
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export type AuthorizationPauseKind = "mcp" | "plugin";
|
|
2
2
|
export type AuthorizationPauseDisposition = "link_already_sent" | "link_sent";
|
|
3
|
-
export type AuthorizationFlowMode = "interactive" | "disabled";
|
|
4
3
|
/**
|
|
5
4
|
* Runtime-owned signal that the current turn must park until the user
|
|
6
5
|
* completes an external authorization step.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";
|
|
10
10
|
import type { Destination, Source } from "@sentry/junior-plugin-api";
|
|
11
11
|
import { type OAuthAuthorization } from "@/chat/oauth-authorization";
|
|
12
|
-
import { AuthorizationPauseError
|
|
12
|
+
import { AuthorizationPauseError } from "@/chat/services/auth-pause";
|
|
13
13
|
import type { ThreadArtifactsState } from "@/chat/state/artifacts";
|
|
14
14
|
import type { ConversationPendingAuthState } from "@/chat/state/conversation";
|
|
15
15
|
import type { PluginDefinition } from "@/chat/plugins/types";
|
|
@@ -32,7 +32,8 @@ export interface McpAuthOrchestrationInput {
|
|
|
32
32
|
getArtifactState: () => ThreadArtifactsState | undefined;
|
|
33
33
|
getMergedArtifactState: () => ThreadArtifactsState;
|
|
34
34
|
recordPendingAuth?: (pendingAuth: ConversationPendingAuthState | undefined) => void | Promise<void>;
|
|
35
|
-
|
|
35
|
+
/** When false, required auth hard-fails instead of pausing for an OAuth link. */
|
|
36
|
+
interactiveAuthEnabled?: boolean;
|
|
36
37
|
authorization?: OAuthAuthorization;
|
|
37
38
|
}
|
|
38
39
|
export interface McpAuthOrchestration {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import type { Destination, Source } from "@sentry/junior-plugin-api";
|
|
14
14
|
import type { UserTokenStore } from "@/chat/credentials/user-token-store";
|
|
15
|
-
import { AuthorizationPauseError
|
|
15
|
+
import { AuthorizationPauseError } from "@/chat/services/auth-pause";
|
|
16
16
|
import type { ConversationPendingAuthState } from "@/chat/state/conversation";
|
|
17
17
|
import { type OAuthAuthorization } from "@/chat/oauth-authorization";
|
|
18
18
|
export declare class PluginAuthorizationPauseError extends AuthorizationPauseError {
|
|
@@ -34,7 +34,8 @@ export interface PluginAuthOrchestrationInput {
|
|
|
34
34
|
userMessage?: string;
|
|
35
35
|
pendingAuth?: ConversationPendingAuthState;
|
|
36
36
|
recordPendingAuth?: (pendingAuth: ConversationPendingAuthState) => void | Promise<void>;
|
|
37
|
-
|
|
37
|
+
/** When false, required auth hard-fails instead of pausing for an OAuth link. */
|
|
38
|
+
interactiveAuthEnabled?: boolean;
|
|
38
39
|
userTokenStore?: UserTokenStore;
|
|
39
40
|
authorization?: OAuthAuthorization;
|
|
40
41
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Passive subscribed-thread reply router policy.
|
|
3
|
+
*
|
|
4
|
+
* Modeled after Junior's Guardian action-review policy shape and the open-source
|
|
5
|
+
* Codex Guardian evidence/prompt pattern:
|
|
6
|
+
* https://github.com/openai/codex/tree/main/codex-rs/core/src/guardian
|
|
7
|
+
*
|
|
8
|
+
* Guardian judges one planned tool action from bounded transcript evidence.
|
|
9
|
+
* This router judges one inbound Slack message from bounded visible
|
|
10
|
+
* user/assistant thread evidence and decides whether Junior should reply.
|
|
11
|
+
*/
|
|
12
|
+
export declare function buildSubscribedReplyRouterPolicy(assistantName: string): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Load turn-session routing from durable conversation metadata.
|
|
3
|
+
*
|
|
4
|
+
* SQL owns destination and session source. Resume paths query those columns
|
|
5
|
+
* directly — no redis fallback and no derived rebuild of missing fields.
|
|
6
|
+
*/
|
|
7
|
+
import type { Destination, Source } from "@sentry/junior-plugin-api";
|
|
8
|
+
import type { ConversationStore } from "@/chat/conversations/store";
|
|
9
|
+
export interface TurnSessionRouting {
|
|
10
|
+
destination: Destination;
|
|
11
|
+
source: Source;
|
|
12
|
+
}
|
|
13
|
+
/** Require conversation destination and session source from SQL. */
|
|
14
|
+
export declare function resolveTurnSessionRouting(args: {
|
|
15
|
+
conversationId: string;
|
|
16
|
+
conversationStore?: ConversationStore;
|
|
17
|
+
}): Promise<TurnSessionRouting>;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Message } from "chat";
|
|
2
2
|
import { type SlackMessageTs } from "@/chat/slack/timestamp";
|
|
3
3
|
/**
|
|
4
4
|
* Preserve the native Slack message timestamp when a synthetic message ID is
|
|
5
5
|
* used for routing or deduplication.
|
|
6
6
|
*/
|
|
7
7
|
export declare function getSlackMessageTs(message: Pick<Message, "id" | "raw">): SlackMessageTs | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* Return the Chat SDK's canonical formatted representation by default.
|
|
10
|
+
* Fall back to plain text only when the message has no formatted content.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getSlackMessageText(message: Pick<Message, "formatted" | "text">): string;
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Parse a Slack workspace subdomain used in `https://{domain}.slack.com` URLs. */
|
|
2
|
+
export declare function parseSlackTeamDomain(value: unknown): string | undefined;
|
|
3
|
+
/**
|
|
4
|
+
* Build a direct Slack archive URL for a thread when the workspace domain is
|
|
5
|
+
* known. Omits a link rather than falling back to `app_redirect`.
|
|
6
|
+
*/
|
|
2
7
|
export declare function buildSlackSourceUrl(args: {
|
|
3
8
|
channelId: string;
|
|
4
|
-
|
|
9
|
+
teamDomain: string;
|
|
10
|
+
threadTs: string;
|
|
5
11
|
}): string | undefined;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Resolve a workspace subdomain via `team.info`, cached by team id. */
|
|
2
|
+
export declare function resolveSlackTeamDomain(teamId: string): Promise<string | undefined>;
|
|
3
|
+
/** Resolve workspace subdomains for the distinct team ids in one API response. */
|
|
4
|
+
export declare function resolveSlackTeamDomains(teamIds: Iterable<string>): Promise<ReadonlyMap<string, string>>;
|
package/dist/chat/source.d.ts
CHANGED
|
@@ -2,14 +2,14 @@ import { type Source } from "@sentry/junior-plugin-api";
|
|
|
2
2
|
/** Source coordinates reduced to the stable locator for one conversation. */
|
|
3
3
|
export type SessionSource = Extract<Source, {
|
|
4
4
|
platform: "local";
|
|
5
|
-
}> |
|
|
5
|
+
}> | Omit<Extract<Source, {
|
|
6
6
|
platform: "slack";
|
|
7
|
-
}>, "messageTs"
|
|
8
|
-
threadTs: string;
|
|
9
|
-
});
|
|
7
|
+
}>, "messageTs">;
|
|
10
8
|
/**
|
|
11
9
|
* Normalize a turn Source into the session-stable locator persisted on a
|
|
12
|
-
* conversation. Drops per-message timestamps
|
|
10
|
+
* conversation. Drops per-message timestamps. Threaded Slack turns keep their
|
|
11
|
+
* thread anchor; channel-level turns (scheduled/event dispatch) keep the
|
|
12
|
+
* channel locator without inventing a threadTs.
|
|
13
13
|
*/
|
|
14
14
|
export declare function normalizeSessionSource(value: Source | undefined): SessionSource | undefined;
|
|
15
15
|
/** Parse a serialized Source into the stable locator stored on a conversation. */
|
|
@@ -26,12 +26,10 @@ export interface AgentTurnSessionRecord {
|
|
|
26
26
|
conversationId: string;
|
|
27
27
|
cumulativeDurationMs: number;
|
|
28
28
|
cumulativeUsage?: AgentTurnUsage;
|
|
29
|
-
destination?: Destination;
|
|
30
29
|
dispatchId?: string;
|
|
31
30
|
dispatchOutcome?: AgentDispatchOutcome;
|
|
32
31
|
/** Provider-owned identifier returned after visible delivery is accepted. */
|
|
33
32
|
resultMessageId?: string;
|
|
34
|
-
source?: Source;
|
|
35
33
|
errorMessage?: string;
|
|
36
34
|
lastProgressAtMs: number;
|
|
37
35
|
loadedSkillNames?: string[];
|
|
@@ -13,6 +13,7 @@ export declare const CONVERSATION_WORK_LEASE_TTL_MS = 90000;
|
|
|
13
13
|
export declare const CONVERSATION_WORK_CHECK_IN_INTERVAL_MS = 15000;
|
|
14
14
|
export declare const CONVERSATION_WORK_STALE_ENQUEUE_MS = 60000;
|
|
15
15
|
export declare const CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS = 5;
|
|
16
|
+
export declare const CONVERSATION_WORK_MAX_RETRIES = 5;
|
|
16
17
|
declare const inboundMessageSourceSchema: z.ZodEnum<{
|
|
17
18
|
slack: "slack";
|
|
18
19
|
plugin: "plugin";
|
|
@@ -46,14 +47,14 @@ export declare const inboundMessageSchema: z.ZodObject<{
|
|
|
46
47
|
defer: "defer";
|
|
47
48
|
interrupt: "interrupt";
|
|
48
49
|
}>;
|
|
49
|
-
destination: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
50
|
+
destination: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
50
51
|
platform: z.ZodLiteral<"slack">;
|
|
51
52
|
teamId: z.ZodString;
|
|
52
53
|
channelId: z.ZodString;
|
|
53
54
|
}, z.core.$strict>, z.ZodObject<{
|
|
54
55
|
platform: z.ZodLiteral<"local">;
|
|
55
56
|
conversationId: z.ZodString;
|
|
56
|
-
}, z.core.$strict>], "platform"
|
|
57
|
+
}, z.core.$strict>], "platform">>;
|
|
57
58
|
inboundMessageId: z.ZodString;
|
|
58
59
|
injectedAtMs: z.ZodOptional<z.ZodNumber>;
|
|
59
60
|
input: z.ZodObject<{
|
|
@@ -84,6 +85,8 @@ export interface ConversationExecution {
|
|
|
84
85
|
inboundMessageIds: string[];
|
|
85
86
|
lastCheckpointAtMs?: number;
|
|
86
87
|
lastEnqueuedAtMs?: number;
|
|
88
|
+
lastProgressAtMs?: number;
|
|
89
|
+
retryCount?: number;
|
|
87
90
|
lease?: Lease;
|
|
88
91
|
pendingCount: number;
|
|
89
92
|
pendingMessages: InboundMessage[];
|
|
@@ -163,7 +166,7 @@ export declare function appendInboundMessage(args: {
|
|
|
163
166
|
/** Mark a conversation runnable when there is no new mailbox message. */
|
|
164
167
|
export declare function requestConversationWork(args: {
|
|
165
168
|
conversationId: string;
|
|
166
|
-
destination
|
|
169
|
+
destination?: Destination;
|
|
167
170
|
nowMs?: number;
|
|
168
171
|
state?: StateAdapter;
|
|
169
172
|
}): Promise<RequestConversationWorkResult>;
|
|
@@ -251,7 +254,7 @@ export declare function ackMessages(args: {
|
|
|
251
254
|
/** Mark the leased conversation as needing another queue-delivered slice. */
|
|
252
255
|
export declare function requestConversationContinuation(args: {
|
|
253
256
|
conversationId: string;
|
|
254
|
-
destination
|
|
257
|
+
destination?: Destination;
|
|
255
258
|
leaseToken: string;
|
|
256
259
|
nowMs?: number;
|
|
257
260
|
state?: StateAdapter;
|
|
@@ -270,25 +273,34 @@ export declare function releaseConversationWork(args: {
|
|
|
270
273
|
nowMs?: number;
|
|
271
274
|
state?: StateAdapter;
|
|
272
275
|
}): Promise<boolean>;
|
|
276
|
+
/** Count one failed execution attempt and stop the conversation when retries are exhausted. */
|
|
277
|
+
export declare function recordConversationRetry(args: {
|
|
278
|
+
conversationId: string;
|
|
279
|
+
leaseToken: string;
|
|
280
|
+
nowMs?: number;
|
|
281
|
+
state?: StateAdapter;
|
|
282
|
+
}): Promise<"lost_lease" | "recorded" | "stopped">;
|
|
273
283
|
/** Finish a leased conversation and report whether runnable work remains. */
|
|
274
284
|
export declare function completeConversationWork(args: {
|
|
275
285
|
conversationId: string;
|
|
276
286
|
leaseToken: string;
|
|
287
|
+
madeProgress?: boolean;
|
|
277
288
|
nowMs?: number;
|
|
278
289
|
state?: StateAdapter;
|
|
279
290
|
}): Promise<"completed" | "lost_lease" | "pending">;
|
|
280
|
-
/** Failure outcome: `lost_lease` (another owner took over), `recorded` (
|
|
291
|
+
/** Failure outcome: `lost_lease` (another owner took over), `recorded` (retry counted), or `skipped` (durable progress was made). */
|
|
281
292
|
export interface AttemptFailure {
|
|
282
293
|
pendingCount: number;
|
|
283
294
|
deadLetteredMessages: InboundMessage[];
|
|
295
|
+
retryCount: number;
|
|
284
296
|
status: "lost_lease" | "recorded" | "skipped";
|
|
285
297
|
}
|
|
286
298
|
/**
|
|
287
|
-
* Record one failed
|
|
288
|
-
*
|
|
289
|
-
*
|
|
299
|
+
* Record one failed execution attempt for pending messages, dead-lettering
|
|
300
|
+
* the conversation when its retry budget or the legacy message delivery limit
|
|
301
|
+
* is exhausted.
|
|
290
302
|
*
|
|
291
|
-
*
|
|
303
|
+
* Retries are counted only when the run made no durable progress: if any
|
|
292
304
|
* attempted message left the mailbox, the remaining pending entries may be
|
|
293
305
|
* deliberate deferrals and are left untouched. Consumed message ids stay in
|
|
294
306
|
* `inboundMessageIds` so source retries remain duplicates.
|
|
@@ -307,12 +319,12 @@ export declare function deadLetterAttempt(args: {
|
|
|
307
319
|
nowMs?: number;
|
|
308
320
|
state?: StateAdapter;
|
|
309
321
|
}): Promise<"failed" | "lost_lease" | "pending">;
|
|
310
|
-
/**
|
|
322
|
+
/** Recover an expired lease, consuming the retry budget under the mutation lock. */
|
|
311
323
|
export declare function clearExpiredConversationLease(args: {
|
|
312
324
|
conversationId: string;
|
|
313
325
|
nowMs?: number;
|
|
314
326
|
state?: StateAdapter;
|
|
315
|
-
}): Promise<
|
|
327
|
+
}): Promise<"not_expired" | "requeued" | "stopped">;
|
|
316
328
|
/** Delete one conversation mailbox and all of its index entries. */
|
|
317
329
|
export declare function deleteConversationState(args: {
|
|
318
330
|
conversationId: string;
|
|
@@ -2,7 +2,7 @@ import type { StateAdapter } from "chat";
|
|
|
2
2
|
import type { ConversationStore } from "@/chat/conversations/store";
|
|
3
3
|
import type { ConversationWorkQueue } from "./queue";
|
|
4
4
|
import * as workState from "./state";
|
|
5
|
-
export { CONVERSATION_ACTIVE_INDEX_KEY, CONVERSATION_BY_ACTIVITY_INDEX_KEY, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS, CONVERSATION_WORK_LEASE_TTL_MS, CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS, CONVERSATION_WORK_STALE_ENQUEUE_MS, isFinalAttempt, isInvalidConversationRecordError, type AgentInput, type AttemptFailure, type AppendAndEnqueueInboundMessageResult, type AppendInboundMessageResult, type Conversation, type ConversationExecution, type ConversationWorkLease, type ConversationWorkState, type ExecutionStatus, type InboundMessage, type InboundMessageDelivery, type Lease, type RequestConversationWorkResult, type Source, type StartConversationWorkAcquired, type StartConversationWorkActive, type StartConversationWorkNoWork, type StartConversationWorkResult, } from "@/chat/task-execution/state";
|
|
5
|
+
export { CONVERSATION_ACTIVE_INDEX_KEY, CONVERSATION_BY_ACTIVITY_INDEX_KEY, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS, CONVERSATION_WORK_LEASE_TTL_MS, CONVERSATION_WORK_MAX_DELIVERY_ATTEMPTS, CONVERSATION_WORK_MAX_RETRIES, CONVERSATION_WORK_STALE_ENQUEUE_MS, isFinalAttempt, isInvalidConversationRecordError, type AgentInput, type AttemptFailure, type AppendAndEnqueueInboundMessageResult, type AppendInboundMessageResult, type Conversation, type ConversationExecution, type ConversationWorkLease, type ConversationWorkState, type ExecutionStatus, type InboundMessage, type InboundMessageDelivery, type Lease, type RequestConversationWorkResult, type Source, type StartConversationWorkAcquired, type StartConversationWorkActive, type StartConversationWorkNoWork, type StartConversationWorkResult, } from "@/chat/task-execution/state";
|
|
6
6
|
import type { AppendAndEnqueueInboundMessageResult, Conversation, InboundMessage } from "@/chat/task-execution/state";
|
|
7
7
|
export type EnsureConversationWakeResult = {
|
|
8
8
|
queueMessageId?: string;
|
|
@@ -20,6 +20,14 @@ export declare function getConversationWorkState(args: {
|
|
|
20
20
|
conversationId: string;
|
|
21
21
|
state?: StateAdapter;
|
|
22
22
|
}): Promise<workState.ConversationWorkState | undefined>;
|
|
23
|
+
/** Count one failed conversation execution and persist its terminal retry state. */
|
|
24
|
+
export declare function recordConversationRetry(args: {
|
|
25
|
+
conversationId: string;
|
|
26
|
+
conversationStore?: ConversationStore;
|
|
27
|
+
leaseToken: string;
|
|
28
|
+
nowMs?: number;
|
|
29
|
+
state?: StateAdapter;
|
|
30
|
+
}): Promise<"stopped" | "lost_lease" | "recorded">;
|
|
23
31
|
/** Count mailbox messages that have not yet reached the conversation event log. */
|
|
24
32
|
export declare function countPendingConversationMessages(conversation: Conversation): number;
|
|
25
33
|
/** Return whether a conversation has pending or resumable execution work. */
|
|
@@ -98,14 +106,6 @@ export declare function drainConversationMailbox(args: Parameters<typeof workSta
|
|
|
98
106
|
conversationId: string;
|
|
99
107
|
createdAtMs: number;
|
|
100
108
|
delivery: "defer" | "interrupt";
|
|
101
|
-
destination: {
|
|
102
|
-
platform: "slack";
|
|
103
|
-
teamId: string;
|
|
104
|
-
channelId: string;
|
|
105
|
-
} | {
|
|
106
|
-
platform: "local";
|
|
107
|
-
conversationId: string;
|
|
108
|
-
};
|
|
109
109
|
inboundMessageId: string;
|
|
110
110
|
input: {
|
|
111
111
|
text: string;
|
|
@@ -116,6 +116,14 @@ export declare function drainConversationMailbox(args: Parameters<typeof workSta
|
|
|
116
116
|
receivedAtMs: number;
|
|
117
117
|
source: "slack" | "plugin" | "local" | "api" | "internal" | "resource_event" | "scheduler";
|
|
118
118
|
attemptCount?: number | undefined;
|
|
119
|
+
destination?: {
|
|
120
|
+
platform: "slack";
|
|
121
|
+
teamId: string;
|
|
122
|
+
channelId: string;
|
|
123
|
+
} | {
|
|
124
|
+
platform: "local";
|
|
125
|
+
conversationId: string;
|
|
126
|
+
} | undefined;
|
|
119
127
|
injectedAtMs?: number | undefined;
|
|
120
128
|
}[]>;
|
|
121
129
|
/** Acknowledge leased mailbox entries after the handler accepts responsibility. */
|
|
@@ -156,6 +164,7 @@ export declare function releaseConversationWork(args: {
|
|
|
156
164
|
export declare function completeConversationWork(args: {
|
|
157
165
|
conversationId: string;
|
|
158
166
|
leaseToken: string;
|
|
167
|
+
madeProgress?: boolean;
|
|
159
168
|
conversationStore?: ConversationStore;
|
|
160
169
|
nowMs?: number;
|
|
161
170
|
state?: StateAdapter;
|
|
@@ -177,13 +186,13 @@ export declare function deadLetterAttempt(args: {
|
|
|
177
186
|
nowMs?: number;
|
|
178
187
|
state?: StateAdapter;
|
|
179
188
|
}): Promise<"pending" | "failed" | "lost_lease">;
|
|
180
|
-
/**
|
|
189
|
+
/** Recover an expired lease and persist the resulting retry state. */
|
|
181
190
|
export declare function clearExpiredConversationLease(args: {
|
|
182
191
|
conversationId: string;
|
|
183
192
|
conversationStore?: ConversationStore;
|
|
184
193
|
nowMs?: number;
|
|
185
194
|
state?: StateAdapter;
|
|
186
|
-
}): Promise<
|
|
195
|
+
}): Promise<"stopped" | "not_expired" | "requeued">;
|
|
187
196
|
/** Remove one conversation from the active index after it is missing or idle. */
|
|
188
197
|
export declare function removeActiveConversation(args: {
|
|
189
198
|
conversationId: string;
|
|
@@ -8,13 +8,13 @@ export interface ConversationWorkerContext {
|
|
|
8
8
|
attempt: InboxAttempt;
|
|
9
9
|
checkIn(): Promise<boolean>;
|
|
10
10
|
conversationId: string;
|
|
11
|
-
destination
|
|
11
|
+
destination?: Destination;
|
|
12
12
|
shouldYield(): boolean;
|
|
13
13
|
}
|
|
14
14
|
export interface InboxAttempt {
|
|
15
15
|
ack(): Promise<void>;
|
|
16
16
|
conversationId: string;
|
|
17
|
-
destination
|
|
17
|
+
destination?: Destination;
|
|
18
18
|
drain(handle: (messages: InboundMessage[]) => Promise<readonly string[] | void>): Promise<InboundMessage[]>;
|
|
19
19
|
isFinalAttempt: boolean;
|
|
20
20
|
messages: InboundMessage[];
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ToolRuntimeContext } from "@/chat/tools/types";
|
|
2
|
+
export declare const SPAWN_AGENT_TOOL_NAME = "spawnAgent";
|
|
3
|
+
/** Create the asynchronous child-agent tool for one parent run. */
|
|
4
|
+
export declare function createSpawnAgentTool(spawnAgent: NonNullable<ToolRuntimeContext["spawnAgent"]>): Omit<import("../definition").AnyToolDefinition, "inputSchema" | "outputSchema" | "prepareArguments" | "execute"> & {
|
|
5
|
+
inputSchema: import("../definition").JsonSchemaObject;
|
|
6
|
+
outputSchema: import("../definition").JsonSchemaObject;
|
|
7
|
+
prepareArguments(args: unknown): {
|
|
8
|
+
task: string;
|
|
9
|
+
name?: string | null | undefined;
|
|
10
|
+
reasoning_level?: "low" | "medium" | "high" | "xhigh" | "none" | null | undefined;
|
|
11
|
+
};
|
|
12
|
+
execute?: ((input: unknown, options: import("../definition").ToolExecuteOptions) => {
|
|
13
|
+
[x: string]: unknown;
|
|
14
|
+
invocation_id: string;
|
|
15
|
+
target?: string | undefined;
|
|
16
|
+
truncated?: boolean | undefined;
|
|
17
|
+
continuation?: {
|
|
18
|
+
arguments: Record<string, unknown>;
|
|
19
|
+
reason?: string | undefined;
|
|
20
|
+
} | undefined;
|
|
21
|
+
} | Promise<{
|
|
22
|
+
[x: string]: unknown;
|
|
23
|
+
invocation_id: string;
|
|
24
|
+
target?: string | undefined;
|
|
25
|
+
truncated?: boolean | undefined;
|
|
26
|
+
continuation?: {
|
|
27
|
+
arguments: Record<string, unknown>;
|
|
28
|
+
reason?: string | undefined;
|
|
29
|
+
} | undefined;
|
|
30
|
+
}>) | undefined;
|
|
31
|
+
};
|
|
@@ -11,6 +11,7 @@ import type { LocalActor, Actor, SlackActor } from "@/chat/actor";
|
|
|
11
11
|
import type { SlackActionToken } from "@/chat/slack/action-token";
|
|
12
12
|
import type { ModelProfile } from "@/chat/model-profile";
|
|
13
13
|
import type { GeneratedArtifactFileRef } from "@/chat/tools/sandbox/file-uploads";
|
|
14
|
+
import type { SpawnAgent } from "@/chat/agent/request";
|
|
14
15
|
interface HandoffControl {
|
|
15
16
|
/** Non-empty catalog of configured targets. */
|
|
16
17
|
profiles: readonly [ModelProfile, ...ModelProfile[]];
|
|
@@ -55,6 +56,7 @@ export interface ToolHooks {
|
|
|
55
56
|
}
|
|
56
57
|
interface BaseToolRuntimeContext {
|
|
57
58
|
handoff?: HandoffControl;
|
|
59
|
+
spawnAgent?: SpawnAgent;
|
|
58
60
|
/**
|
|
59
61
|
* Opaque Junior conversation/session identity for this turn.
|
|
60
62
|
* Interactive Slack turns use `slack:{channelId}:{threadTs}`.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/chat/experimental.ts
|
|
2
|
+
var EXPERIMENTAL_FEATURES = ["subagents"];
|
|
3
|
+
var EXPERIMENTAL_FEATURE_SET = new Set(EXPERIMENTAL_FEATURES);
|
|
4
|
+
var configuredExperimental = {};
|
|
5
|
+
function isExperimentalFeature(value) {
|
|
6
|
+
return EXPERIMENTAL_FEATURE_SET.has(value);
|
|
7
|
+
}
|
|
8
|
+
function setExperimentalFeatures(config) {
|
|
9
|
+
const previous = { ...configuredExperimental };
|
|
10
|
+
if (config === void 0) {
|
|
11
|
+
configuredExperimental = {};
|
|
12
|
+
return previous;
|
|
13
|
+
}
|
|
14
|
+
const next = {};
|
|
15
|
+
for (const [rawName, enabled] of Object.entries(config)) {
|
|
16
|
+
if (enabled === void 0) {
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
if (typeof enabled !== "boolean") {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`experimental.${rawName} must be a boolean when provided`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
if (!isExperimentalFeature(rawName)) {
|
|
25
|
+
throw new Error(
|
|
26
|
+
`experimental.${rawName} is not a known experimental feature. Known features: ${EXPERIMENTAL_FEATURES.join(", ")}`
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
next[rawName] = enabled;
|
|
30
|
+
}
|
|
31
|
+
configuredExperimental = next;
|
|
32
|
+
return previous;
|
|
33
|
+
}
|
|
34
|
+
function getExperimentalFeatures() {
|
|
35
|
+
return { ...configuredExperimental };
|
|
36
|
+
}
|
|
37
|
+
function isExperimentalFeatureEnabled(feature) {
|
|
38
|
+
return configuredExperimental[feature] === true;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
EXPERIMENTAL_FEATURES,
|
|
43
|
+
setExperimentalFeatures,
|
|
44
|
+
getExperimentalFeatures,
|
|
45
|
+
isExperimentalFeatureEnabled
|
|
46
|
+
};
|