replicas-cli 0.2.603 → 0.2.604

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.
Files changed (2) hide show
  1. package/dist/index.cjs +1884 -1842
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -22358,58 +22358,6 @@ function date4(params) {
22358
22358
  // ../node_modules/.bun/zod@4.4.3/node_modules/zod/v4/classic/external.js
22359
22359
  config(en_default());
22360
22360
 
22361
- // ../shared/src/analytics/types.ts
22362
- var agentCredentialSnapshotSchema = external_exports.object({
22363
- method: external_exports.enum(CREDENTIAL_METHOD),
22364
- scope: external_exports.enum(CREDENTIAL_SCOPE),
22365
- revision: external_exports.string().optional()
22366
- });
22367
- function isAuthMethod(value) {
22368
- return typeof value === "string" && Object.values(CREDENTIAL_METHOD).some((method) => method === value);
22369
- }
22370
- function isCredentialScope(value) {
22371
- return typeof value === "string" && CREDENTIAL_SCOPES.some((scope) => scope === value);
22372
- }
22373
-
22374
- // ../shared/src/event.ts
22375
- var CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE = "claude-partial-message";
22376
- function coerceClaudePartialMessagePayload(payload) {
22377
- const streamId = payload.streamId;
22378
- if (typeof streamId !== "string" || !streamId) return null;
22379
- const parentToolUseId = payload.parent_tool_use_id;
22380
- const text = typeof payload.text === "string" && payload.text ? payload.text : void 0;
22381
- const thinking = typeof payload.thinking === "string" && payload.thinking ? payload.thinking : void 0;
22382
- if (!text && !thinking) return null;
22383
- return {
22384
- streamId,
22385
- parent_tool_use_id: typeof parentToolUseId === "string" ? parentToolUseId : null,
22386
- ...text ? { text } : {},
22387
- ...thinking ? { thinking } : {},
22388
- status: payload.status === "completed" ? "completed" : "in_progress"
22389
- };
22390
- }
22391
- var USER_MESSAGE_ID_PAYLOAD_KEY = "replicasMessageId";
22392
- var CODEX_ASP_ITEM_ID_PAYLOAD_KEY = "codexAspItemId";
22393
- var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
22394
- var CHAT_INTERRUPTED_EVENT_TYPE = "replicas-interrupted";
22395
- var AUTH_FALLBACK_EVENT_TYPE = "auth-fallback";
22396
- function coerceAuthFallbackPayload(payload) {
22397
- const { provider, reason, exhaustedMethod, exhaustedScope, candidateMethod, candidateScope, status, detail } = payload;
22398
- if (typeof provider !== "string" || !isValidCodingAgentProvider(provider) || reason !== "rate_limit" && reason !== "out_of_credits" || !isAuthMethod(exhaustedMethod) || !isAuthMethod(candidateMethod) || !isCredentialScope(candidateScope) || status !== "switched" && status !== "failed") {
22399
- return null;
22400
- }
22401
- return {
22402
- provider,
22403
- reason,
22404
- exhaustedMethod,
22405
- ...isCredentialScope(exhaustedScope) ? { exhaustedScope } : {},
22406
- candidateMethod,
22407
- candidateScope,
22408
- status,
22409
- ...typeof detail === "string" && detail ? { detail } : {}
22410
- };
22411
- }
22412
-
22413
22361
  // ../shared/src/aster.ts
22414
22362
  var ASTER_MODELS = [
22415
22363
  "kimi-k3",
@@ -22663,319 +22611,529 @@ function classifyCanvasFilename(filename) {
22663
22611
  return CANVAS_KIND_BY_EXTENSION[ext] ?? { kind: "other", mimeType: "application/octet-stream" };
22664
22612
  }
22665
22613
 
22666
- // ../shared/src/pricing.ts
22667
- var AUTOMATED_SOURCES = ["api", "automation"];
22668
- var AUTOMATED_SOURCES_SQL = AUTOMATED_SOURCES.map((s) => `'${s}'`).join(", ");
22669
- var PLANS = {
22670
- trial: {
22671
- id: "trial",
22672
- name: "Free Trial",
22673
- monthlyPrice: 0,
22674
- seatPriceCents: 0,
22675
- features: [
22676
- "14 days free, no credit card required",
22677
- "Unlimited manual workspace minutes",
22678
- "15,000 automated workspace minutes",
22679
- "Unlimited repositories and environments",
22680
- "Unlimited automations",
22681
- "Warm pools and warm hooks",
22682
- "API access"
22683
- ]
22684
- },
22685
- developer: {
22686
- id: "developer",
22687
- name: "Developer",
22688
- monthlyPrice: 120,
22689
- seatPriceCents: 12e3,
22690
- features: [
22691
- "Unlimited manual workspace minutes",
22692
- "5,000 automated workspace minutes per month",
22693
- "Up to 10 repositories",
22694
- "Up to 15 environments",
22695
- "Up to 5 automations",
22696
- "Warm pools and warm hooks",
22697
- "API access",
22698
- "Mobile app"
22699
- ]
22700
- },
22701
- team: {
22702
- id: "team",
22703
- name: "Team",
22704
- monthlyPrice: 300,
22705
- seatPriceCents: 3e4,
22706
- features: [
22707
- "Unlimited manual workspace minutes",
22708
- "15,000 automated workspace minutes per month",
22709
- "Unlimited repositories",
22710
- "Unlimited automations",
22711
- "Warm pools and warm hooks",
22712
- "Higher API rate limits",
22713
- "Access to a static egress IP",
22714
- "Auto-upgraded sandbox resources (4 vCPU, 32 GB disk, 16 GB memory)",
22715
- "Shared Slack support channel"
22716
- ]
22717
- },
22718
- enterprise: {
22719
- id: "enterprise",
22720
- name: "Enterprise",
22721
- monthlyPrice: 0,
22722
- seatPriceCents: 0,
22723
- features: [
22724
- "Unlimited manual workspace minutes",
22725
- "Unlimited automated workspace minutes",
22726
- "Unlimited automations",
22727
- "Custom warm hooks and pools",
22728
- "Custom API rates",
22729
- "Custom rate limits",
22730
- "Shared Slack support channel",
22731
- "SOC 2",
22732
- "Data Processing Agreement (DPA)",
22733
- "SCIM user provisioning",
22734
- "Custom security and privacy settings"
22735
- ]
22736
- }
22737
- };
22738
- var TEAM_PLAN = PLANS.team;
22739
- var ENTERPRISE_PLAN = PLANS.enterprise;
22614
+ // ../shared/src/chat-fork.ts
22615
+ var quotaLimitKindSchema = external_exports.enum(["rate_limit", "out_of_credits"]);
22616
+ var chatForkInfoSchema = external_exports.object({
22617
+ source: external_exports.object({
22618
+ chatId: external_exports.string().min(1),
22619
+ provider: external_exports.enum(VALID_AGENT_PROVIDERS),
22620
+ title: external_exports.string()
22621
+ }),
22622
+ trigger: external_exports.enum(["manual", "auto"]),
22623
+ reason: quotaLimitKindSchema.optional(),
22624
+ state: external_exports.enum(["preparing", "ready", "failed"]),
22625
+ error: external_exports.string().optional(),
22626
+ startedAt: external_exports.string(),
22627
+ completedAt: external_exports.string().optional()
22628
+ });
22629
+ var forkChatRequestSchema = external_exports.object({
22630
+ provider: external_exports.enum(VALID_AGENT_PROVIDERS),
22631
+ model: external_exports.string().min(1).optional(),
22632
+ thinkingLevel: external_exports.enum(VALID_THINKING_LEVELS).optional(),
22633
+ title: external_exports.string().min(1).max(200).optional(),
22634
+ id: external_exports.string().uuid().optional(),
22635
+ clientRequestId: external_exports.string().min(1).max(128).optional(),
22636
+ enableInteractiveTools: external_exports.boolean().optional(),
22637
+ // Server-injected by the monolith from the authenticated request; never trusted from clients.
22638
+ senderUserId: external_exports.string().optional(),
22639
+ senderEmail: external_exports.string().optional(),
22640
+ senderDisplayName: external_exports.string().optional(),
22641
+ senderAvatarUrl: external_exports.string().optional()
22642
+ });
22643
+ var chatForkFallbackResponseSchema = external_exports.object({
22644
+ provider: external_exports.enum(VALID_AGENT_PROVIDERS).nullable(),
22645
+ model: external_exports.string().min(1).optional(),
22646
+ thinkingLevel: external_exports.enum(VALID_THINKING_LEVELS).optional()
22647
+ });
22648
+ var agentQuotaExhaustedPayloadSchema = external_exports.object({
22649
+ provider: external_exports.enum(VALID_CODING_AGENT_PROVIDERS),
22650
+ reason: quotaLimitKindSchema,
22651
+ detail: external_exports.string().optional()
22652
+ });
22740
22653
 
22741
- // ../shared/src/port.ts
22742
- function isValidPort(port) {
22743
- return Number.isInteger(port) && Number(port) >= 1 && Number(port) <= 65535;
22654
+ // ../shared/src/analytics/types.ts
22655
+ var agentCredentialSnapshotSchema = external_exports.object({
22656
+ method: external_exports.enum(CREDENTIAL_METHOD),
22657
+ scope: external_exports.enum(CREDENTIAL_SCOPE),
22658
+ revision: external_exports.string().optional()
22659
+ });
22660
+ function isAuthMethod(value) {
22661
+ return typeof value === "string" && Object.values(CREDENTIAL_METHOD).some((method) => method === value);
22744
22662
  }
22745
- function parsePort(port) {
22746
- if (!/^\d+$/.test(port)) throw new Error("Port must be a number between 1 and 65535");
22747
- const portNumber = Number(port);
22748
- if (!isValidPort(portNumber)) throw new Error("Port must be a number between 1 and 65535");
22749
- return portNumber;
22663
+ function isCredentialScope(value) {
22664
+ return typeof value === "string" && CREDENTIAL_SCOPES.some((scope) => scope === value);
22750
22665
  }
22751
22666
 
22752
- // ../shared/src/models-dev.ts
22753
- var MODELS_DEV_CACHE_MS = 5 * 6e4;
22754
- var modelsDevCatalogSchema = external_exports.record(external_exports.string(), external_exports.object({
22755
- models: external_exports.record(external_exports.string(), external_exports.object({
22756
- id: external_exports.string().optional(),
22757
- name: external_exports.string(),
22758
- description: external_exports.string().optional(),
22759
- status: external_exports.enum(["alpha", "beta", "deprecated"]).optional(),
22760
- cost: external_exports.object({
22761
- input: external_exports.number().finite().nonnegative().optional(),
22762
- output: external_exports.number().finite().nonnegative().optional(),
22763
- cache_read: external_exports.number().finite().nonnegative().optional(),
22764
- cache_write: external_exports.number().finite().nonnegative().optional()
22765
- }).optional()
22766
- }))
22767
- }));
22768
-
22769
- // ../shared/src/sandbox.ts
22770
- var SANDBOX_LIFECYCLE = {
22771
- AUTO_STOP_MINUTES: 60,
22772
- AUTO_ARCHIVE_MINUTES: 60 * 24 * 7,
22773
- AUTO_DELETE_MINUTES: -1,
22774
- SSH_TOKEN_EXPIRATION_MINUTES: 3 * 60
22775
- };
22776
- var SANDBOX_PATHS = {
22777
- HOME_DIR: "/home/user",
22778
- WORKSPACES_DIR: "/home/user/workspaces",
22779
- REPLICAS_DIR: "/home/user/.replicas",
22780
- ENGINE_INIT_GATE: "/home/user/.replicas/engine-init-gate",
22781
- REPLICAS_CANVAS_DIR: "/home/user/.replicas/canvas",
22782
- REPLICAS_RUNTIME_ENV_FILE: "/home/user/.replicas/runtime-env.sh"
22783
- };
22784
- var WORKSPACE_SIZES = ["small", "large"];
22785
- var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
22667
+ // ../shared/src/event.ts
22668
+ var CLAUDE_PARTIAL_MESSAGE_EVENT_TYPE = "claude-partial-message";
22669
+ function coerceClaudePartialMessagePayload(payload) {
22670
+ const streamId = payload.streamId;
22671
+ if (typeof streamId !== "string" || !streamId) return null;
22672
+ const parentToolUseId = payload.parent_tool_use_id;
22673
+ const text = typeof payload.text === "string" && payload.text ? payload.text : void 0;
22674
+ const thinking = typeof payload.thinking === "string" && payload.thinking ? payload.thinking : void 0;
22675
+ if (!text && !thinking) return null;
22676
+ return {
22677
+ streamId,
22678
+ parent_tool_use_id: typeof parentToolUseId === "string" ? parentToolUseId : null,
22679
+ ...text ? { text } : {},
22680
+ ...thinking ? { thinking } : {},
22681
+ status: payload.status === "completed" ? "completed" : "in_progress"
22682
+ };
22683
+ }
22684
+ var USER_MESSAGE_ID_PAYLOAD_KEY = "replicasMessageId";
22685
+ var CODEX_ASP_ITEM_ID_PAYLOAD_KEY = "codexAspItemId";
22686
+ var CODEX_QUOTA_STATUS_EVENT_TYPE = "codex-quota-status";
22687
+ var CHAT_INTERRUPTED_EVENT_TYPE = "replicas-interrupted";
22688
+ var AUTH_FALLBACK_EVENT_TYPE = "auth-fallback";
22689
+ function coerceAuthFallbackPayload(payload) {
22690
+ const { provider, reason, exhaustedMethod, exhaustedScope, candidateMethod, candidateScope, status, detail } = payload;
22691
+ if (typeof provider !== "string" || !isValidCodingAgentProvider(provider) || reason !== "rate_limit" && reason !== "out_of_credits" || !isAuthMethod(exhaustedMethod) || !isAuthMethod(candidateMethod) || !isCredentialScope(candidateScope) || status !== "switched" && status !== "failed") {
22692
+ return null;
22693
+ }
22694
+ return {
22695
+ provider,
22696
+ reason,
22697
+ exhaustedMethod,
22698
+ ...isCredentialScope(exhaustedScope) ? { exhaustedScope } : {},
22699
+ candidateMethod,
22700
+ candidateScope,
22701
+ status,
22702
+ ...typeof detail === "string" && detail ? { detail } : {}
22703
+ };
22704
+ }
22786
22705
 
22787
- // ../shared/src/egress.ts
22788
- var EGRESS_CIPHER = "2022-blake3-aes-256-gcm";
22789
- var EGRESS_KEY_BYTES = 32;
22790
- var EGRESS_SHADOWSOCKS_MODE = "tcp_and_udp";
22791
- var EGRESS_SHADOWSOCKS_VERSION = "1.24.0";
22792
- var EGRESS_SHADOWSOCKS_SHA256 = "0d84f5f350ec99396867d718f146fc3810975b2a7cd06192f158d96bdef460e7";
22793
- var EGRESS_PORT_RANGE_START = 8400;
22794
- var EGRESS_PORT_RANGE_END = 8499;
22795
- var EGRESS_EXIT_IP_ENDPOINTS = [
22796
- "https://checkip.amazonaws.com",
22797
- "https://api.ipify.org",
22798
- "https://icanhazip.com"
22799
- ];
22800
- var EGRESS_DIRECT_ROUTES = [
22801
- "10.0.0.0/8",
22802
- "172.16.0.0/12",
22803
- "192.168.0.0/16",
22804
- "169.254.0.0/16",
22805
- "100.64.0.0/10"
22806
- ];
22807
- var EGRESS_NEW_CONN_RATE = 100;
22808
- var EGRESS_NEW_CONN_BURST = 200;
22809
- var EGRESS_UDP_RATE = 500;
22810
- var EGRESS_UDP_BURST = 1e3;
22811
- var EGRESS_OUT_CONN_RATE = 1e3;
22812
- var EGRESS_OUT_CONN_BURST = 2e3;
22813
- var EGRESS_DNS_SERVERS = ["1.1.1.1", "8.8.8.8"];
22814
- var EGRESS_SHELL_CONFIG = [
22815
- `EGRESS_PORT_RANGE_START=${EGRESS_PORT_RANGE_START}`,
22816
- `EGRESS_PORT_RANGE_END=${EGRESS_PORT_RANGE_END}`,
22817
- `EGRESS_SHADOWSOCKS_VERSION=${EGRESS_SHADOWSOCKS_VERSION}`,
22818
- `EGRESS_SHADOWSOCKS_SHA256=${EGRESS_SHADOWSOCKS_SHA256}`,
22819
- `EGRESS_CIPHER=${EGRESS_CIPHER}`,
22820
- `EGRESS_KEY_BYTES=${EGRESS_KEY_BYTES}`,
22821
- `EGRESS_MODE=${EGRESS_SHADOWSOCKS_MODE}`,
22822
- `EGRESS_DNS_SERVERS="${EGRESS_DNS_SERVERS.join(" ")}"`,
22823
- `EGRESS_EXIT_IP_ENDPOINTS="${EGRESS_EXIT_IP_ENDPOINTS.join(" ")}"`,
22824
- `EGRESS_DIRECT_ROUTES="${EGRESS_DIRECT_ROUTES.join(" ")}"`,
22825
- `EGRESS_NEW_CONN_RATE=${EGRESS_NEW_CONN_RATE}`,
22826
- `EGRESS_NEW_CONN_BURST=${EGRESS_NEW_CONN_BURST}`,
22827
- `EGRESS_UDP_RATE=${EGRESS_UDP_RATE}`,
22828
- `EGRESS_UDP_BURST=${EGRESS_UDP_BURST}`,
22829
- `EGRESS_OUT_CONN_RATE=${EGRESS_OUT_CONN_RATE}`,
22830
- `EGRESS_OUT_CONN_BURST=${EGRESS_OUT_CONN_BURST}`
22831
- ].join("\n");
22832
- var EGRESS_TUNNEL_SERVICE = "replicas-egress.service";
22833
- var EGRESS_TUNNEL_INTERFACE = "rpxtun0";
22834
- var EGRESS_PATHS = {
22835
- /**
22836
- * Under /run, which is tmpfs. The key therefore never touches disk and cannot
22837
- * be carried into a filesystem snapshot or a paused sandbox's disk image. It
22838
- * does not survive a reboot either. A full-memory resume can reuse it only
22839
- * while the root-owned revision still matches the current profile and the
22840
- * running service and route are healthy.
22841
- */
22842
- CONFIG_DIR: "/run/replicas-egress",
22843
- CONFIG_FILE: "/run/replicas-egress/config.json",
22844
- REVISION_FILE: "/run/replicas-egress/revision",
22845
- /** Persistent: this records the workspace's original resolver so the tunnel can put it back, and must outlive a reboot. */
22846
- RESOLV_BACKUP: "/var/lib/replicas-egress-resolv.conf.orig",
22847
- UNIT_FILE: `/etc/systemd/system/${EGRESS_TUNNEL_SERVICE}`,
22848
- UP_SCRIPT: "/usr/local/lib/replicas-egress-up",
22849
- DOWN_SCRIPT: "/usr/local/lib/replicas-egress-down"
22850
- };
22851
- var EGRESS_WORKSPACE_CONFIG_FILE = "/opt/replicas/egress-config.env";
22852
- var EGRESS_WORKSPACE_SHELL_VARS = {
22853
- EGRESS_WORKSPACE_CONFIG_FILE,
22854
- EGRESS_TUNNEL_SERVICE,
22855
- EGRESS_TUNNEL_INTERFACE,
22856
- EGRESS_DNS_SERVERS: EGRESS_DNS_SERVERS.join(" "),
22857
- EGRESS_EXIT_IP_ENDPOINTS: EGRESS_EXIT_IP_ENDPOINTS.join(" ")
22858
- };
22706
+ // ../shared/src/engine/v1.ts
22707
+ var createChatRequestSchema = external_exports.object({
22708
+ id: external_exports.string().uuid().optional(),
22709
+ createdAt: external_exports.string().datetime().optional(),
22710
+ provider: external_exports.enum(VALID_AGENT_PROVIDERS),
22711
+ title: external_exports.string().min(1).optional(),
22712
+ parentChatId: external_exports.string().uuid().optional(),
22713
+ clientRequestId: external_exports.string().min(1).max(128).optional()
22714
+ });
22715
+ var sendChatMessageRequestSchema = external_exports.object({
22716
+ messageId: external_exports.string().min(1).optional(),
22717
+ submittedAt: external_exports.string().datetime().optional(),
22718
+ message: external_exports.string().min(1),
22719
+ model: external_exports.string().optional(),
22720
+ customInstructions: external_exports.string().optional(),
22721
+ planMode: external_exports.boolean().optional(),
22722
+ images: external_exports.array(external_exports.object({
22723
+ type: external_exports.literal("image"),
22724
+ source: external_exports.discriminatedUnion("type", [
22725
+ external_exports.object({ type: external_exports.literal("base64"), media_type: external_exports.enum(IMAGE_MEDIA_TYPES), data: external_exports.string().min(1) }),
22726
+ external_exports.object({ type: external_exports.literal("url"), url: external_exports.string().url() })
22727
+ ])
22728
+ })).optional(),
22729
+ canvasUploadIds: external_exports.array(external_exports.string().min(1)).optional(),
22730
+ thinkingLevel: external_exports.enum(VALID_THINKING_LEVELS).optional(),
22731
+ goalMode: external_exports.boolean().optional(),
22732
+ fastMode: external_exports.boolean().optional(),
22733
+ enableInteractiveTools: external_exports.boolean().optional(),
22734
+ type: external_exports.string().min(1).optional(),
22735
+ merge: external_exports.boolean().optional(),
22736
+ idempotencyKey: external_exports.string().min(1).max(128).optional(),
22737
+ automationId: external_exports.string().optional(),
22738
+ environmentId: external_exports.string().min(1).optional(),
22739
+ senderUserId: external_exports.string().optional(),
22740
+ senderEmail: external_exports.string().optional(),
22741
+ senderDisplayName: external_exports.string().optional(),
22742
+ senderAvatarUrl: external_exports.string().optional(),
22743
+ errorNotificationTarget: external_exports.discriminatedUnion("type", [
22744
+ external_exports.object({ type: external_exports.literal("slack") }),
22745
+ external_exports.object({ type: external_exports.literal("linear"), sessionId: external_exports.string().min(1) }),
22746
+ external_exports.object({
22747
+ type: external_exports.literal("code_host"),
22748
+ provider: external_exports.enum(["github", "gitlab"]),
22749
+ resource: external_exports.enum(["issue", "pull_request"]),
22750
+ repositoryId: external_exports.string().min(1),
22751
+ resourceNumber: external_exports.number().int().positive()
22752
+ }),
22753
+ external_exports.object({ type: external_exports.literal("automation"), executionId: external_exports.string().min(1) })
22754
+ ]).optional()
22755
+ }).passthrough();
22756
+ function normalizeCodexAspTranscriptStatus(status, failed = false) {
22757
+ if (failed || status === "failed" || status === "declined") return "failed";
22758
+ if (status === "completed") return "completed";
22759
+ return "in_progress";
22760
+ }
22761
+ function getCodexAspTurnResponse(turn) {
22762
+ const finalAnswer = turn.items.find((item) => item.type === "agentMessage" && item.phase === "final_answer");
22763
+ if (finalAnswer) return finalAnswer;
22764
+ if (normalizeCodexAspTranscriptStatus(turn.status) === "in_progress") return null;
22765
+ return turn.items.findLast((item) => item.type === "agentMessage") ?? null;
22766
+ }
22859
22767
 
22860
- // ../shared/src/urls.ts
22861
- var DEFAULT_WEB_APP_URL = "https://replicas.dev";
22862
- var HOME_PATH = "/home";
22863
- var DASHBOARD_PATH = "/dashboard";
22864
- var WEB_APP_PATHS = {
22865
- home: HOME_PATH,
22866
- onboarding: `${HOME_PATH}/onboarding`,
22867
- workspaces: `${HOME_PATH}/workspace`,
22868
- automations: `${HOME_PATH}/automations`,
22869
- environment: `${HOME_PATH}/environment`,
22870
- analytics: `${HOME_PATH}/analytics`,
22871
- memory: `${HOME_PATH}/memory`,
22872
- dashboard: DASHBOARD_PATH,
22873
- agents: `${DASHBOARD_PATH}/agents`,
22874
- integrations: `${DASHBOARD_PATH}/integrations`,
22875
- integrationsGithub: `${DASHBOARD_PATH}/integrations/github`,
22876
- integrationsGitlab: `${DASHBOARD_PATH}/integrations/gitlab`,
22877
- settings: `${DASHBOARD_PATH}/settings`,
22878
- billing: `${DASHBOARD_PATH}/billing`,
22879
- members: `${DASHBOARD_PATH}/members`,
22880
- "audit-logs": `${DASHBOARD_PATH}/audit-logs`,
22881
- accountAgents: `${DASHBOARD_PATH}/account/agents`,
22882
- accountProfile: `${DASHBOARD_PATH}/account/profile`,
22883
- accountConnections: `${DASHBOARD_PATH}/account/connections`,
22884
- accountIntegrations: `${DASHBOARD_PATH}/account/integrations`,
22885
- accountApiKeys: `${DASHBOARD_PATH}/account/api-keys`,
22886
- accountPreferences: `${DASHBOARD_PATH}/account/preferences`,
22887
- fullscreenWorkspaces: "/workspaces"
22888
- };
22889
- var LEGACY_WEB_APP_PATHS = {
22890
- app: "/app",
22891
- dashboardWorkspaces: `${DASHBOARD_PATH}/workspaces`
22892
- };
22893
- function getWorkspaceDashboardUrl(workspaceId, options = {}) {
22894
- const { encodeWorkspaceId = true, mode, mediaId } = options;
22895
- const workspaceIdValue = encodeWorkspaceId ? encodeURIComponent(workspaceId) : workspaceId;
22896
- const params = [];
22897
- if (mode) params.push(`mode=${encodeURIComponent(mode)}`);
22898
- if (mediaId) params.push(`media=${encodeURIComponent(mediaId)}`);
22899
- const query = params.length > 0 ? `?${params.join("&")}` : "";
22900
- return `${DEFAULT_WEB_APP_URL}${WEB_APP_PATHS.fullscreenWorkspaces}/${workspaceIdValue}${query}`;
22768
+ // ../shared/src/display-message/types.ts
22769
+ var BACKGROUND_TASK_SUBTYPES = /* @__PURE__ */ new Set([
22770
+ "task_started",
22771
+ "task_progress",
22772
+ "task_updated",
22773
+ "task_notification"
22774
+ ]);
22775
+ function isBackgroundTaskSubtype(subtype) {
22776
+ return typeof subtype === "string" && BACKGROUND_TASK_SUBTYPES.has(subtype);
22901
22777
  }
22902
- function getOrganizationMediaPath(organizationId, mediaId) {
22903
- return `/organizations/${encodeURIComponent(organizationId)}/media/${encodeURIComponent(mediaId)}`;
22778
+ function optionalString(value) {
22779
+ return typeof value === "string" ? value : void 0;
22904
22780
  }
22905
- function getOrganizationMediaUrl(organizationId, mediaId, webAppUrl = DEFAULT_WEB_APP_URL) {
22906
- return `${webAppUrl}${getOrganizationMediaPath(organizationId, mediaId)}`;
22781
+ function coerceBackgroundTaskPayload(payload) {
22782
+ if (!isRecord(payload)) return null;
22783
+ const subtype = payload.subtype;
22784
+ if (!isBackgroundTaskSubtype(subtype)) return null;
22785
+ const taskId = payload.task_id;
22786
+ if (typeof taskId !== "string" || !taskId) return null;
22787
+ const patch = isRecord(payload.patch) ? {
22788
+ status: optionalString(payload.patch.status),
22789
+ description: optionalString(payload.patch.description),
22790
+ error: optionalString(payload.patch.error),
22791
+ isBackgrounded: optionalBoolean(payload.patch.is_backgrounded)
22792
+ } : void 0;
22793
+ const usage = isRecord(payload.usage) ? {
22794
+ totalTokens: optionalNumber(payload.usage.total_tokens),
22795
+ toolUses: optionalNumber(payload.usage.tool_uses),
22796
+ durationMs: optionalNumber(payload.usage.duration_ms)
22797
+ } : void 0;
22798
+ return {
22799
+ subtype,
22800
+ taskId,
22801
+ description: optionalString(payload.description),
22802
+ summary: optionalString(payload.summary),
22803
+ outputFile: optionalString(payload.output_file),
22804
+ status: optionalString(payload.status),
22805
+ taskType: optionalString(payload.task_type),
22806
+ workflowName: optionalString(payload.workflow_name),
22807
+ prompt: optionalString(payload.prompt),
22808
+ lastToolName: optionalString(payload.last_tool_name),
22809
+ usage,
22810
+ ambient: optionalBoolean(payload.ambient),
22811
+ patch
22812
+ };
22813
+ }
22814
+ function optionalNumber(value) {
22815
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
22816
+ }
22817
+ function optionalBoolean(value) {
22818
+ return typeof value === "boolean" ? value : void 0;
22819
+ }
22820
+ function normalizeBackgroundTaskStatus(status) {
22821
+ if (status === "completed" || status === "failed" || status === "stopped") {
22822
+ return status;
22823
+ }
22824
+ if (status === "killed") {
22825
+ return "stopped";
22826
+ }
22827
+ return "in_progress";
22907
22828
  }
22908
22829
 
22909
- // ../shared/src/default-skills/replicas-agent/abilities/computer.ts
22910
- var SECTION = `### Computer use (Linux desktop control)
22911
- Drive the workspace's Linux desktop - open a browser, click, type, scroll, screenshot, record - via the \`replicas computer\` CLI. Every Replicas workspace boots with a live H.264 and Opus stream published as an authenticated preview, so a \`Desktop\` tab is always available in the dashboard. Use \`replicas computer info\` to get the viewer URL when you want to share it (e.g. point a user on Slack at the live stream).
22912
-
22913
- **Reference:** \`references/COMPUTER-USE.md\`
22914
-
22915
- Use this when:
22916
- - A task requires interacting with a website, web app, or desktop application that has no usable API
22917
- - You want the user to watch the agent work - they can open the \`Desktop\` tab, or you can share the URL from \`replicas computer info\`
22918
- - You're testing UI changes in a browser before reporting them as done
22919
- - You want to record a screen capture of a task as proof to share back`;
22920
- var REFERENCE = `# Computer use (Linux desktop control)
22921
-
22922
- Every Replicas workspace boots with a full Linux desktop stack - Xvfb (1920\xD71080), openbox, tint2, PulseAudio, ffmpeg, and Google Chrome. The engine auto-registers the H.264 and Opus viewer (port 6080) as an authenticated preview at startup, so the dashboard \`Desktop\` tab is live the moment the workspace is up. You drive the desktop through the \`replicas computer\` CLI.
22923
-
22924
- Use this for anything the user can't reasonably do via an API - clicking around web apps, filling forms, testing UI changes, dragging files between desktop apps, recording a walkthrough.
22925
-
22926
- ## When to use it
22927
-
22928
- - **Prefer real APIs first.** If a task has a CLI or HTTP API (GitHub, Linear, Slack, Replicas itself), use that. Driving a UI is slower, flakier, and less auditable.
22929
- - **Use it when there's no API**: testing a frontend you just changed, navigating a vendor portal, demonstrating a flow on video.
22930
- - **Use it when the user wants to watch.** The dashboard already shows a \`Desktop\` tab. If you're talking to the user somewhere else (e.g. Slack), call \`replicas computer info\` to get the viewer URL and paste it in.
22931
-
22932
- ## Never use raw \`xdotool\` / \`scrot\` / \`ffmpeg\` directly
22933
-
22934
- The CLI is the canonical surface. Chat transcripts that show \`DISPLAY=:99 xdotool key Return\` are noisy and break when the underlying stack changes. Always call the CLI:
22935
-
22936
- \`\`\`bash
22937
- # DO
22938
- replicas computer key Return
22939
- replicas computer observe /tmp/state.png
22940
-
22941
- # DON'T
22942
- DISPLAY=:99 xdotool key Return
22943
- scrot /tmp/state.png
22944
- \`\`\`
22830
+ // ../shared/src/display-message/constants.ts
22831
+ var USER_MESSAGE_MATCH_GRACE_PERIOD_MS = 3e4;
22945
22832
 
22946
- ## Quickstart
22833
+ // ../shared/src/json.ts
22834
+ function safeJsonParse(str, fallback) {
22835
+ try {
22836
+ return JSON.parse(str);
22837
+ } catch {
22838
+ return fallback;
22839
+ }
22840
+ }
22947
22841
 
22948
- \`\`\`bash
22949
- # 1) (Optional) Get the live viewer URL so you can share it with the user.
22950
- # The desktop is already running and the preview is already registered -
22951
- # this just looks up the URL.
22952
- replicas computer info
22842
+ // ../shared/src/display-message/parsers/utils.ts
22843
+ var INTERRUPTED_MESSAGE_REGEX = /^\[Request interrupted by user.*\]$/;
22844
+ function userMessageImages(value) {
22845
+ if (!Array.isArray(value)) return void 0;
22846
+ const images = value.filter((item) => isRecord(item) && item.type === "image" && typeof item.mediaType === "string" && typeof item.data === "string");
22847
+ return images.length > 0 ? images : void 0;
22848
+ }
22849
+ function stringifyDisplayValue(value) {
22850
+ if (value === void 0 || value === null) return void 0;
22851
+ if (typeof value === "string") return value;
22852
+ try {
22853
+ return JSON.stringify(value, null, 2);
22854
+ } catch {
22855
+ return String(value);
22856
+ }
22857
+ }
22858
+ function upsertDisplayMessage(messages, message) {
22859
+ const index = messages.findIndex((candidate) => candidate.id === message.id);
22860
+ if (index === -1) messages.push(message);
22861
+ else messages[index] = message;
22862
+ }
22863
+ function applyToolInputEvent(event, messages, provider, state, questionFilter) {
22864
+ if (event.type === "replicas-tool-input-request" && typeof event.payload.toolUseId === "string" && typeof event.payload.requestId === "string" && (Array.isArray(event.payload.options) || Array.isArray(event.payload.questions))) {
22865
+ const resolved = state.resolutions.get(event.payload.toolUseId);
22866
+ const inputRequest = {
22867
+ requestId: event.payload.requestId,
22868
+ status: resolved?.selectionId === "aborted" || resolved?.selectionId === "declined" ? "aborted" : resolved ? "resolved" : "pending",
22869
+ ...Array.isArray(event.payload.options) ? { options: event.payload.options } : {},
22870
+ ...Array.isArray(event.payload.questions) ? { questions: questionFilter ? event.payload.questions.filter(questionFilter) : event.payload.questions } : {},
22871
+ ...resolved ? { selectionId: resolved.selectionId } : {},
22872
+ ...resolved?.selectionSummary ? { selectionSummary: resolved.selectionSummary } : {}
22873
+ };
22874
+ state.requests.set(event.payload.toolUseId, inputRequest);
22875
+ const message = messages.find((candidate) => candidate.type === "tool_call" && candidate.id === `${provider}-tool-${event.payload.toolUseId}`);
22876
+ if (message) message.inputRequest = inputRequest;
22877
+ return true;
22878
+ }
22879
+ if (event.type === "replicas-tool-input-resolved" && typeof event.payload.toolUseId === "string" && typeof event.payload.selectionId === "string") {
22880
+ const resolved = {
22881
+ selectionId: event.payload.selectionId,
22882
+ ...typeof event.payload.selectionSummary === "string" ? { selectionSummary: event.payload.selectionSummary } : {}
22883
+ };
22884
+ state.resolutions.set(event.payload.toolUseId, resolved);
22885
+ const inputRequest = state.requests.get(event.payload.toolUseId);
22886
+ if (inputRequest) {
22887
+ inputRequest.status = resolved.selectionId === "aborted" || resolved.selectionId === "declined" ? "aborted" : "resolved";
22888
+ inputRequest.selectionId = resolved.selectionId;
22889
+ if (resolved.selectionSummary) inputRequest.selectionSummary = resolved.selectionSummary;
22890
+ }
22891
+ return true;
22892
+ }
22893
+ return false;
22894
+ }
22895
+ function parseSkillName(tool, input) {
22896
+ if (typeof tool !== "string") return null;
22897
+ const normalizedTool = tool.toLowerCase();
22898
+ if (normalizedTool.startsWith("skill.")) return tool.slice("skill.".length) || null;
22899
+ if (normalizedTool !== "skill") return null;
22900
+ const parsedInput = typeof input === "string" ? safeJsonParse(input, null) : input;
22901
+ if (!isRecord(parsedInput)) return null;
22902
+ const skillName = parsedInput.skill ?? parsedInput.name;
22903
+ return typeof skillName === "string" && skillName ? skillName : null;
22904
+ }
22905
+ function skillNameFromFilePath(path6) {
22906
+ const skillName = skillNameFromSkillRootPath(path6);
22907
+ if (skillName) return skillName;
22908
+ const parts = path6.split(/[\\/]+/).filter(Boolean);
22909
+ if (parts.length < 2 || parts[parts.length - 1].toLowerCase() !== "skill.md") return null;
22910
+ const fallbackSkillName = parts[parts.length - 2];
22911
+ return fallbackSkillName && fallbackSkillName.toLowerCase() !== "skills" ? fallbackSkillName : null;
22912
+ }
22913
+ function skillNameFromSkillRootPath(path6) {
22914
+ const parts = path6.split(/[\\/]+/).filter(Boolean);
22915
+ const skillsIndex = parts.findLastIndex((part) => part.toLowerCase() === "skills");
22916
+ if (skillsIndex === -1) return null;
22917
+ const firstSkillPart = parts[skillsIndex + 1];
22918
+ const skillName = firstSkillPart?.toLowerCase() === ".system" ? parts[skillsIndex + 2] : firstSkillPart;
22919
+ return skillName && skillName.toLowerCase() !== "skill.md" ? skillName : null;
22920
+ }
22921
+ function parseSkillFilePath(input) {
22922
+ const parsedInput = typeof input === "string" ? safeJsonParse(input, null) : input;
22923
+ if (!isRecord(parsedInput)) return null;
22924
+ const path6 = [parsedInput.path, parsedInput.filePath, parsedInput.file_path].find((value) => typeof value === "string" && value.length > 0);
22925
+ if (!path6) return null;
22926
+ return skillNameFromFilePath(path6);
22927
+ }
22928
+ function skillNameFromToolCall(tool, input) {
22929
+ const directSkillName = parseSkillName(tool, input);
22930
+ if (directSkillName) return directSkillName;
22931
+ if (typeof tool !== "string" || !["read", "read_file", "readfile"].includes(tool.toLowerCase())) return null;
22932
+ return parseSkillFilePath(input);
22933
+ }
22934
+ function createCallDisplayMessage(message) {
22935
+ const skillName = message.server.toLowerCase() === "skills" ? message.tool : skillNameFromToolCall(message.tool, message.input);
22936
+ if (skillName === null) return { ...message, type: "tool_call" };
22937
+ return {
22938
+ id: message.id,
22939
+ type: "skill",
22940
+ skillName,
22941
+ status: message.status,
22942
+ timestamp: message.timestamp
22943
+ };
22944
+ }
22945
+ function insertDisplayMessageByTimestamp(messages, message) {
22946
+ const messageMs = parseTimestampMs(message.timestamp);
22947
+ let index = messages.length;
22948
+ while (index > 0 && parseTimestampMs(messages[index - 1].timestamp) > messageMs) index--;
22949
+ messages.splice(index, 0, message);
22950
+ }
22953
22951
 
22954
- # 2) Launch a browser on the workspace display.
22955
- replicas computer launch chrome https://news.ycombinator.com
22952
+ // ../shared/src/display-message/format.ts
22953
+ function unquoteShellArg(value) {
22954
+ const trimmed = value.trim();
22955
+ if (trimmed.length < 2) return trimmed;
22956
+ if (trimmed[0] === "'" && trimmed[trimmed.length - 1] === "'") {
22957
+ return trimmed.slice(1, -1).replace(/'\\''/g, "'");
22958
+ }
22959
+ if (trimmed[0] === '"' && trimmed[trimmed.length - 1] === '"') {
22960
+ return trimmed.slice(1, -1).replace(/\\(["\\$`])/g, "$1");
22961
+ }
22962
+ return trimmed;
22963
+ }
22964
+ function shellPayload(command) {
22965
+ const match = command.trim().match(/^(?:\S*\/)?(?:ba|z|c|fi)?sh\s+-(?:[a-zA-Z]*c[a-zA-Z]*|c)\s+([\s\S]+)$/);
22966
+ return match ? unquoteShellArg(match[1]) : command.trim();
22967
+ }
22968
+ function splitShellWords(command) {
22969
+ const words = [];
22970
+ let word = "";
22971
+ let quote = null;
22972
+ let escaped = false;
22973
+ for (const char of command) {
22974
+ if (escaped) {
22975
+ word += char;
22976
+ escaped = false;
22977
+ continue;
22978
+ }
22979
+ if (char === "\\" && quote !== "'") {
22980
+ escaped = true;
22981
+ continue;
22982
+ }
22983
+ if (quote) {
22984
+ if (char === quote) quote = null;
22985
+ else word += char;
22986
+ continue;
22987
+ }
22988
+ if (char === '"' || char === "'") {
22989
+ quote = char;
22990
+ continue;
22991
+ }
22992
+ if (/\s/.test(char)) {
22993
+ if (word) {
22994
+ words.push(word);
22995
+ word = "";
22996
+ }
22997
+ continue;
22998
+ }
22999
+ word += char;
23000
+ }
23001
+ if (word) words.push(word);
23002
+ return words;
23003
+ }
23004
+ function commandWords(command) {
23005
+ const payload = shellPayload(command);
23006
+ if (!payload || /[;|<>]|&&/.test(payload)) return null;
23007
+ return splitShellWords(payload);
23008
+ }
23009
+ function skillNamesFromCommand(command) {
23010
+ const words = commandWords(command);
23011
+ if (!words) return [];
23012
+ return Array.from(new Set(words.flatMap((word) => {
23013
+ const skillName = skillNameFromSkillRootPath(word);
23014
+ return skillName ? [skillName] : [];
23015
+ })));
23016
+ }
22956
23017
 
22957
- # 3) Capture the settled viewport and native accessibility tree together.
22958
- replicas computer browser-state /tmp/state.png --full
23018
+ // ../shared/src/urls.ts
23019
+ var DEFAULT_WEB_APP_URL = "https://replicas.dev";
23020
+ var HOME_PATH = "/home";
23021
+ var DASHBOARD_PATH = "/dashboard";
23022
+ var WEB_APP_PATHS = {
23023
+ home: HOME_PATH,
23024
+ onboarding: `${HOME_PATH}/onboarding`,
23025
+ workspaces: `${HOME_PATH}/workspace`,
23026
+ automations: `${HOME_PATH}/automations`,
23027
+ environment: `${HOME_PATH}/environment`,
23028
+ analytics: `${HOME_PATH}/analytics`,
23029
+ memory: `${HOME_PATH}/memory`,
23030
+ dashboard: DASHBOARD_PATH,
23031
+ agents: `${DASHBOARD_PATH}/agents`,
23032
+ integrations: `${DASHBOARD_PATH}/integrations`,
23033
+ integrationsGithub: `${DASHBOARD_PATH}/integrations/github`,
23034
+ integrationsGitlab: `${DASHBOARD_PATH}/integrations/gitlab`,
23035
+ settings: `${DASHBOARD_PATH}/settings`,
23036
+ billing: `${DASHBOARD_PATH}/billing`,
23037
+ members: `${DASHBOARD_PATH}/members`,
23038
+ "audit-logs": `${DASHBOARD_PATH}/audit-logs`,
23039
+ accountAgents: `${DASHBOARD_PATH}/account/agents`,
23040
+ accountProfile: `${DASHBOARD_PATH}/account/profile`,
23041
+ accountConnections: `${DASHBOARD_PATH}/account/connections`,
23042
+ accountIntegrations: `${DASHBOARD_PATH}/account/integrations`,
23043
+ accountApiKeys: `${DASHBOARD_PATH}/account/api-keys`,
23044
+ accountPreferences: `${DASHBOARD_PATH}/account/preferences`,
23045
+ fullscreenWorkspaces: "/workspaces"
23046
+ };
23047
+ var LEGACY_WEB_APP_PATHS = {
23048
+ app: "/app",
23049
+ dashboardWorkspaces: `${DASHBOARD_PATH}/workspaces`
23050
+ };
23051
+ function getWorkspaceDashboardUrl(workspaceId, options = {}) {
23052
+ const { encodeWorkspaceId = true, mode, mediaId } = options;
23053
+ const workspaceIdValue = encodeWorkspaceId ? encodeURIComponent(workspaceId) : workspaceId;
23054
+ const params = [];
23055
+ if (mode) params.push(`mode=${encodeURIComponent(mode)}`);
23056
+ if (mediaId) params.push(`media=${encodeURIComponent(mediaId)}`);
23057
+ const query = params.length > 0 ? `?${params.join("&")}` : "";
23058
+ return `${DEFAULT_WEB_APP_URL}${WEB_APP_PATHS.fullscreenWorkspaces}/${workspaceIdValue}${query}`;
23059
+ }
23060
+ function getOrganizationMediaPath(organizationId, mediaId) {
23061
+ return `/organizations/${encodeURIComponent(organizationId)}/media/${encodeURIComponent(mediaId)}`;
23062
+ }
23063
+ function getOrganizationMediaUrl(organizationId, mediaId, webAppUrl = DEFAULT_WEB_APP_URL) {
23064
+ return `${webAppUrl}${getOrganizationMediaPath(organizationId, mediaId)}`;
23065
+ }
22959
23066
 
22960
- # 4) Batch actions that use refs from the same state, then capture the semantic diff.
22961
- replicas computer browser-batch '[{"action":"fill","ref":17,"value":"hello"},{"action":"click","ref":42}]'
22962
- replicas computer browser-state /tmp/after-click.png
23067
+ // ../shared/src/default-skills/replicas-agent/abilities/computer.ts
23068
+ var SECTION = `### Computer use (Linux desktop control)
23069
+ Drive the workspace's Linux desktop - open a browser, click, type, scroll, screenshot, record - via the \`replicas computer\` CLI. Every Replicas workspace boots with a live H.264 and Opus stream published as an authenticated preview, so a \`Desktop\` tab is always available in the dashboard. Use \`replicas computer info\` to get the viewer URL when you want to share it (e.g. point a user on Slack at the live stream).
22963
23070
 
22964
- # 5) (Optional) Record a screencap to share back.
22965
- replicas computer record start /tmp/demo.mp4 --fps 60
22966
- # ... do stuff ...
22967
- replicas computer record stop
22968
- replicas media upload /tmp/demo.mp4 --name "Demo recording"
22969
- \`\`\`
23071
+ **Reference:** \`references/COMPUTER-USE.md\`
22970
23072
 
22971
- For any external destination, continue with the required delivery flow in \`MEDIA.md\`.
23073
+ Use this when:
23074
+ - A task requires interacting with a website, web app, or desktop application that has no usable API
23075
+ - You want the user to watch the agent work - they can open the \`Desktop\` tab, or you can share the URL from \`replicas computer info\`
23076
+ - You're testing UI changes in a browser before reporting them as done
23077
+ - You want to record a screen capture of a task as proof to share back`;
23078
+ var REFERENCE = `# Computer use (Linux desktop control)
22972
23079
 
22973
- ## Command reference
23080
+ Every Replicas workspace boots with a full Linux desktop stack - Xvfb (1920\xD71080), openbox, tint2, PulseAudio, ffmpeg, and Google Chrome. The engine auto-registers the H.264 and Opus viewer (port 6080) as an authenticated preview at startup, so the dashboard \`Desktop\` tab is live the moment the workspace is up. You drive the desktop through the \`replicas computer\` CLI.
22974
23081
 
22975
- ### \`replicas computer info\`
22976
- Prints the live desktop viewer URL (\`https://6080-<hash>.replicas.dev/\`). The engine auto-registers this authenticated preview on startup; \`info\` just looks it up. Use it when you want to share the live stream URL with the user out-of-band (e.g. in a Slack reply) - the dashboard \`Desktop\` tab already points at the same URL automatically.
23082
+ Use this for anything the user can't reasonably do via an API - clicking around web apps, filling forms, testing UI changes, dragging files between desktop apps, recording a walkthrough.
22977
23083
 
22978
- If invoked very early in the workspace lifecycle, \`info\` will poll briefly while the engine finishes registering the preview, then error if it's still not available.
23084
+ ## When to use it
23085
+
23086
+ - **Prefer real APIs first.** If a task has a CLI or HTTP API (GitHub, Linear, Slack, Replicas itself), use that. Driving a UI is slower, flakier, and less auditable.
23087
+ - **Use it when there's no API**: testing a frontend you just changed, navigating a vendor portal, demonstrating a flow on video.
23088
+ - **Use it when the user wants to watch.** The dashboard already shows a \`Desktop\` tab. If you're talking to the user somewhere else (e.g. Slack), call \`replicas computer info\` to get the viewer URL and paste it in.
23089
+
23090
+ ## Never use raw \`xdotool\` / \`scrot\` / \`ffmpeg\` directly
23091
+
23092
+ The CLI is the canonical surface. Chat transcripts that show \`DISPLAY=:99 xdotool key Return\` are noisy and break when the underlying stack changes. Always call the CLI:
23093
+
23094
+ \`\`\`bash
23095
+ # DO
23096
+ replicas computer key Return
23097
+ replicas computer observe /tmp/state.png
23098
+
23099
+ # DON'T
23100
+ DISPLAY=:99 xdotool key Return
23101
+ scrot /tmp/state.png
23102
+ \`\`\`
23103
+
23104
+ ## Quickstart
23105
+
23106
+ \`\`\`bash
23107
+ # 1) (Optional) Get the live viewer URL so you can share it with the user.
23108
+ # The desktop is already running and the preview is already registered -
23109
+ # this just looks up the URL.
23110
+ replicas computer info
23111
+
23112
+ # 2) Launch a browser on the workspace display.
23113
+ replicas computer launch chrome https://news.ycombinator.com
23114
+
23115
+ # 3) Capture the settled viewport and native accessibility tree together.
23116
+ replicas computer browser-state /tmp/state.png --full
23117
+
23118
+ # 4) Batch actions that use refs from the same state, then capture the semantic diff.
23119
+ replicas computer browser-batch '[{"action":"fill","ref":17,"value":"hello"},{"action":"click","ref":42}]'
23120
+ replicas computer browser-state /tmp/after-click.png
23121
+
23122
+ # 5) (Optional) Record a screencap to share back.
23123
+ replicas computer record start /tmp/demo.mp4 --fps 60
23124
+ # ... do stuff ...
23125
+ replicas computer record stop
23126
+ replicas media upload /tmp/demo.mp4 --name "Demo recording"
23127
+ \`\`\`
23128
+
23129
+ For any external destination, continue with the required delivery flow in \`MEDIA.md\`.
23130
+
23131
+ ## Command reference
23132
+
23133
+ ### \`replicas computer info\`
23134
+ Prints the live desktop viewer URL (\`https://6080-<hash>.replicas.dev/\`). The engine auto-registers this authenticated preview on startup; \`info\` just looks it up. Use it when you want to share the live stream URL with the user out-of-band (e.g. in a Slack reply) - the dashboard \`Desktop\` tab already points at the same URL automatically.
23135
+
23136
+ If invoked very early in the workspace lifecycle, \`info\` will poll briefly while the engine finishes registering the preview, then error if it's still not available.
22979
23137
 
22980
23138
  ### \`replicas computer status\`
22981
23139
  Checks and repairs the desktop bridge, then prints which desktop services are running and the active preview URL (if any). Useful for debugging when a tool call seems to be doing nothing.
@@ -24524,7 +24682,9 @@ var DEFAULT_USER_PREFERENCES = {
24524
24682
  open_links_in_desktop_app: false,
24525
24683
  default_fast_mode: false,
24526
24684
  agent_defaults: { ...EMPTY_AGENT_DEFAULT_SETTINGS },
24527
- credential_priority: null
24685
+ credential_priority: null,
24686
+ chat_fork_fallbacks_enabled: false,
24687
+ chat_fork_fallback_providers: []
24528
24688
  };
24529
24689
  function defaultReplicasAgentAbilities() {
24530
24690
  const abilities = {};
@@ -24554,980 +24714,24 @@ function removeReplicasInstructions(text) {
24554
24714
  return removeTag(text, REPLICAS_INSTRUCTIONS_TAG);
24555
24715
  }
24556
24716
 
24557
- // ../shared/src/replicas-config.ts
24558
- var import_yaml = __toESM(require_dist());
24559
-
24560
- // ../shared/src/warm-hooks.ts
24561
- var DEFAULT_WARM_HOOK_TIMEOUT_MS = 60 * 60 * 1e3;
24562
- var MAX_WARM_HOOK_TIMEOUT_MS = 120 * 60 * 1e3;
24563
- var DEFAULT_START_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
24564
- var HOOK_EXEC_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
24565
-
24566
- // ../shared/src/replicas-config.ts
24567
- var REPLICAS_CONFIG_FILENAMES = ["replicas.json", "replicas.yaml", "replicas.yml"];
24568
-
24569
- // ../shared/src/headless-agent.ts
24570
- var headlessAgentRequestBaseSchema = external_exports.object({
24571
- agent: external_exports.custom(
24572
- (value) => typeof value === "string" && isValidCodingAgentProvider(value)
24573
- ),
24574
- model: external_exports.string().min(1),
24575
- prompt: external_exports.string(),
24576
- workingDirectory: external_exports.string().min(1),
24577
- timeoutSeconds: external_exports.number().int().min(1).max(1800).default(300),
24578
- codexOauthTokens: external_exports.object({ accessToken: external_exports.string(), accountId: external_exports.string() }).optional()
24579
- });
24580
- var headlessAgentInputFileSchema = external_exports.object({
24581
- path: external_exports.string().min(1),
24582
- downloadUrl: external_exports.url()
24583
- });
24584
- var headlessAgentOutputFileSchema = external_exports.object({
24585
- path: external_exports.string().min(1),
24586
- uploadUrl: external_exports.url(),
24587
- contentType: external_exports.string().min(1),
24588
- minChars: external_exports.number().int().positive().optional(),
24589
- maxChars: external_exports.number().int().positive().optional()
24590
- });
24591
- var headlessAgentRequestSchema = external_exports.discriminatedUnion("mode", [
24592
- headlessAgentRequestBaseSchema.extend({
24593
- mode: external_exports.literal("structured"),
24594
- outputSchema: external_exports.record(external_exports.string(), external_exports.json())
24595
- }),
24596
- headlessAgentRequestBaseSchema.extend({
24597
- mode: external_exports.literal("filesystem"),
24598
- inputFiles: external_exports.array(headlessAgentInputFileSchema),
24599
- outputFiles: external_exports.array(headlessAgentOutputFileSchema),
24600
- sensitiveValues: external_exports.array(external_exports.string())
24601
- })
24602
- ]);
24603
- var headlessFilesystemAgentResultSchema = external_exports.object({
24604
- message: external_exports.string(),
24605
- files: external_exports.record(external_exports.string(), external_exports.object({
24606
- sha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
24607
- sizeBytes: external_exports.number().int().nonnegative()
24608
- }))
24609
- });
24610
-
24611
- // ../shared/src/errors.ts
24612
- var TRANSIENT_NETWORK_ERROR_PATTERNS = [
24613
- /socket connection was closed unexpectedly/,
24614
- /fetch failed/,
24615
- /network error/,
24616
- /network request failed/,
24617
- /econnreset/,
24618
- /econnrefused/,
24619
- /etimedout/,
24620
- /enotfound/,
24621
- /eai_again/,
24622
- /und_err_socket/,
24623
- /und_err_connect_timeout/,
24624
- /headers timeout/,
24625
- /body timeout/,
24626
- /connection reset/,
24627
- /connection closed/,
24628
- /closed unexpectedly/
24629
- ];
24630
- var HTTP_CONTEXT_PATTERN = /(api error|request|response|status|http)/;
24631
- var HTTP_STATUS_PATTERN = /(^|[^0-9])([1-5][0-9]{2})(?=$|[^0-9])/g;
24632
- function isTransientErrorText(text, options = {}) {
24633
- const normalized = text.toLowerCase();
24634
- if (!normalized) return false;
24635
- if (TRANSIENT_NETWORK_ERROR_PATTERNS.some((pattern) => pattern.test(normalized))) return true;
24636
- if (options.includeRateLimit && /rate limit/.test(normalized)) return true;
24637
- if (options.extraPatterns?.some((pattern) => pattern.test(normalized))) return true;
24638
- const httpStatuses = new Set(options.httpStatuses ?? []);
24639
- const httpStatusClasses = new Set(options.httpStatusClasses ?? []);
24640
- if (httpStatuses.size === 0 && httpStatusClasses.size === 0) return false;
24641
- if (options.requireHttpContext !== false && !HTTP_CONTEXT_PATTERN.test(normalized)) return false;
24642
- for (const match of normalized.matchAll(HTTP_STATUS_PATTERN)) {
24643
- const status = Number(match[2]);
24644
- if (httpStatuses.has(status) || httpStatusClasses.has(Math.floor(status / 100))) return true;
24645
- }
24646
- return false;
24717
+ // ../shared/src/display-message/parsers/codex-parser.ts
24718
+ function getStatusFromExitCode(exitCode) {
24719
+ return exitCode === 0 ? "completed" : "failed";
24647
24720
  }
24648
-
24649
- // ../shared/src/cli-version.ts
24650
- var CLI_VERSION = "0.2.603";
24651
-
24652
- // ../shared/src/version.ts
24653
- function compareVersions(v1, v2) {
24654
- const parts1 = v1.split(".").map(Number);
24655
- const parts2 = v2.split(".").map(Number);
24656
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
24657
- const num1 = parts1[i] || 0;
24658
- const num2 = parts2[i] || 0;
24659
- if (num1 > num2) return 1;
24660
- if (num1 < num2) return -1;
24661
- }
24662
- return 0;
24721
+ function getPayloadString(event, key) {
24722
+ const value = event.payload?.[key];
24723
+ return typeof value === "string" ? value : null;
24663
24724
  }
24664
-
24665
- // ../shared/src/engine/v1.ts
24666
- var createChatRequestSchema = external_exports.object({
24667
- id: external_exports.string().uuid().optional(),
24668
- createdAt: external_exports.string().datetime().optional(),
24669
- provider: external_exports.enum(VALID_AGENT_PROVIDERS),
24670
- title: external_exports.string().min(1).optional(),
24671
- parentChatId: external_exports.string().uuid().optional(),
24672
- clientRequestId: external_exports.string().min(1).max(128).optional()
24673
- });
24674
- var sendChatMessageRequestSchema = external_exports.object({
24675
- messageId: external_exports.string().min(1).optional(),
24676
- submittedAt: external_exports.string().datetime().optional(),
24677
- message: external_exports.string().min(1),
24678
- model: external_exports.string().optional(),
24679
- customInstructions: external_exports.string().optional(),
24680
- planMode: external_exports.boolean().optional(),
24681
- images: external_exports.array(external_exports.object({
24682
- type: external_exports.literal("image"),
24683
- source: external_exports.discriminatedUnion("type", [
24684
- external_exports.object({ type: external_exports.literal("base64"), media_type: external_exports.enum(IMAGE_MEDIA_TYPES), data: external_exports.string().min(1) }),
24685
- external_exports.object({ type: external_exports.literal("url"), url: external_exports.string().url() })
24686
- ])
24687
- })).optional(),
24688
- canvasUploadIds: external_exports.array(external_exports.string().min(1)).optional(),
24689
- thinkingLevel: external_exports.enum(VALID_THINKING_LEVELS).optional(),
24690
- goalMode: external_exports.boolean().optional(),
24691
- fastMode: external_exports.boolean().optional(),
24692
- enableInteractiveTools: external_exports.boolean().optional(),
24693
- type: external_exports.string().min(1).optional(),
24694
- merge: external_exports.boolean().optional(),
24695
- idempotencyKey: external_exports.string().min(1).max(128).optional(),
24696
- automationId: external_exports.string().optional(),
24697
- environmentId: external_exports.string().min(1).optional(),
24698
- senderUserId: external_exports.string().optional(),
24699
- senderEmail: external_exports.string().optional(),
24700
- senderDisplayName: external_exports.string().optional(),
24701
- senderAvatarUrl: external_exports.string().optional(),
24702
- errorNotificationTarget: external_exports.discriminatedUnion("type", [
24703
- external_exports.object({ type: external_exports.literal("slack") }),
24704
- external_exports.object({ type: external_exports.literal("linear"), sessionId: external_exports.string().min(1) }),
24705
- external_exports.object({
24706
- type: external_exports.literal("code_host"),
24707
- provider: external_exports.enum(["github", "gitlab"]),
24708
- resource: external_exports.enum(["issue", "pull_request"]),
24709
- repositoryId: external_exports.string().min(1),
24710
- resourceNumber: external_exports.number().int().positive()
24711
- }),
24712
- external_exports.object({ type: external_exports.literal("automation"), executionId: external_exports.string().min(1) })
24713
- ]).optional()
24714
- }).passthrough();
24715
- function normalizeCodexAspTranscriptStatus(status, failed = false) {
24716
- if (failed || status === "failed" || status === "declined") return "failed";
24717
- if (status === "completed") return "completed";
24718
- return "in_progress";
24725
+ function displayId(event, eventIndex, prefix) {
24726
+ const stableId = getPayloadString(event, USER_MESSAGE_ID_PAYLOAD_KEY) ?? getPayloadString(event, CODEX_ASP_ITEM_ID_PAYLOAD_KEY);
24727
+ return stableId ? `${prefix}-${stableId}` : `${prefix}-${event.timestamp}-${eventIndex}`;
24719
24728
  }
24720
- function getCodexAspTurnResponse(turn) {
24721
- const finalAnswer = turn.items.find((item) => item.type === "agentMessage" && item.phase === "final_answer");
24722
- if (finalAnswer) return finalAnswer;
24723
- if (normalizeCodexAspTranscriptStatus(turn.status) === "in_progress") return null;
24724
- return turn.items.findLast((item) => item.type === "agentMessage") ?? null;
24725
- }
24726
-
24727
- // ../shared/src/routes/plugin-catalog.ts
24728
- var EXPLICIT_PLUGIN_CATALOG = [
24729
- {
24730
- id: "attio",
24731
- backend: "composio",
24732
- toolkit: "attio",
24733
- authType: "oauth",
24734
- category: "business",
24735
- name: "Attio",
24736
- description: "Manage CRM records, people, companies, lists, and notes."
24737
- },
24738
- {
24739
- id: "calendly",
24740
- backend: "composio",
24741
- toolkit: "calendly",
24742
- authType: "oauth",
24743
- category: "productivity",
24744
- name: "Calendly",
24745
- description: "Manage event types, scheduled events, invitees, and scheduling links."
24746
- },
24747
- {
24748
- id: "clickhouse",
24749
- backend: "composio",
24750
- toolkit: "clickhouse",
24751
- authType: "basic",
24752
- category: "data",
24753
- name: "ClickHouse",
24754
- description: "Manage databases, tables, schemas, and queries."
24755
- },
24756
- {
24757
- id: "cloudflare",
24758
- backend: "composio",
24759
- toolkit: "cloudflare_api_key",
24760
- authType: "api_key",
24761
- category: "data",
24762
- name: "Cloudflare",
24763
- description: "Manage zones, DNS records, analytics, and security settings."
24764
- },
24765
- {
24766
- id: "e2b",
24767
- toolkit: "e2b",
24768
- backend: "native",
24769
- authType: "api_key",
24770
- category: "data",
24771
- name: "E2B",
24772
- description: "Create secure cloud sandboxes and run commands in them."
24773
- },
24774
- {
24775
- id: "gmail",
24776
- backend: "composio",
24777
- toolkit: "gmail",
24778
- authType: "oauth",
24779
- category: "productivity",
24780
- name: "Gmail",
24781
- description: "Manage messages, threads, attachments, labels, and mailbox settings."
24782
- },
24783
- {
24784
- id: "googleads",
24785
- backend: "composio",
24786
- toolkit: "googleads",
24787
- authType: "oauth",
24788
- category: "data",
24789
- name: "Google Ads",
24790
- description: "Manage campaigns, customers, accounts, and reporting data."
24791
- },
24792
- {
24793
- id: "googledocs",
24794
- backend: "composio",
24795
- toolkit: "googledocs",
24796
- authType: "oauth",
24797
- category: "productivity",
24798
- name: "Google Docs",
24799
- description: "Manage documents, structure, comments, and exported content."
24800
- },
24801
- {
24802
- id: "googledrive",
24803
- backend: "composio",
24804
- toolkit: "googledrive",
24805
- authType: "oauth",
24806
- category: "productivity",
24807
- name: "Google Drive",
24808
- description: "Manage files, folders, metadata, permissions, and shared drives."
24809
- },
24810
- {
24811
- id: "googlesearch",
24812
- backend: "composio",
24813
- toolkit: "google_search_console",
24814
- authType: "oauth",
24815
- category: "data",
24816
- name: "Google Search",
24817
- description: "Manage Search Console performance, indexing, sites, and sitemaps."
24818
- },
24819
- {
24820
- id: "googlesheets",
24821
- backend: "composio",
24822
- toolkit: "googlesheets",
24823
- authType: "oauth",
24824
- category: "productivity",
24825
- name: "Google Sheets",
24826
- description: "Manage spreadsheets, worksheets, ranges, tables, and charts."
24827
- },
24828
- {
24829
- id: "linkedin",
24830
- backend: "composio",
24831
- toolkit: "linkedin",
24832
- authType: "oauth",
24833
- category: "business",
24834
- name: "LinkedIn",
24835
- description: "Manage profiles, organizations, posts, and professional activity."
24836
- },
24837
- {
24838
- id: "modal",
24839
- backend: "native",
24840
- toolkit: "modal",
24841
- authType: "basic",
24842
- category: "data",
24843
- name: "Modal",
24844
- description: "Invoke deployed functions through Modal compute."
24845
- },
24846
- {
24847
- id: "posthog",
24848
- backend: "composio",
24849
- toolkit: "posthog",
24850
- authType: "api_key",
24851
- category: "data",
24852
- name: "PostHog",
24853
- description: "Manage projects, events, insights, dashboards, flags, and recordings."
24854
- },
24855
- {
24856
- id: "planetscale",
24857
- toolkit: "planetscale",
24858
- backend: "native",
24859
- authType: "oauth",
24860
- category: "data",
24861
- name: "PlanetScale",
24862
- description: "Manage organizations, databases, branches, deploy requests, and Insights."
24863
- },
24864
- {
24865
- id: "stripe",
24866
- backend: "composio",
24867
- toolkit: "stripe",
24868
- authType: "oauth",
24869
- category: "business",
24870
- name: "Stripe",
24871
- description: "Manage customers, payments, invoices, subscriptions, and balances."
24872
- },
24873
- {
24874
- id: "pangram",
24875
- backend: "native",
24876
- toolkit: "pangram",
24877
- authType: "api_key",
24878
- category: "data",
24879
- name: "Pangram",
24880
- description: "Analyze text for AI generation and plagiarism signals."
24881
- },
24882
- {
24883
- id: "vercel",
24884
- backend: "native",
24885
- toolkit: "vercel",
24886
- authType: "api_key",
24887
- category: "data",
24888
- name: "Vercel",
24889
- description: "Manage projects, deployments, logs, domains, and infrastructure."
24890
- },
24891
- {
24892
- id: "turbopuffer",
24893
- backend: "native",
24894
- toolkit: "turbopuffer",
24895
- authType: "api_key",
24896
- category: "data",
24897
- name: "turbopuffer",
24898
- description: "Query and manage turbopuffer namespaces."
24899
- },
24900
- {
24901
- id: "vanta",
24902
- backend: "native",
24903
- toolkit: "vanta",
24904
- authType: "credentials",
24905
- category: "data",
24906
- name: "Vanta",
24907
- description: "Review and manage controls, tests, evidence, vendors, risks, and compliance data.",
24908
- scopes: ["environment"]
24909
- }
24910
- ];
24911
- var HOSTED_COMPOSIO_PLUGIN_DEFINITIONS = [
24912
- ["notion", "notion", "OAUTH2", true, "productivity", "Notion"],
24913
- ["jira", "jira", "OAUTH2", true, "productivity", "Jira"],
24914
- ["confluence", "confluence", "OAUTH2", true, "productivity", "Confluence"],
24915
- ["googlecalendar", "googlecalendar", "OAUTH2", true, "productivity", "Google Calendar"],
24916
- ["microsoftteams", "microsoft_teams", "OAUTH2", true, "productivity", "Microsoft Teams"],
24917
- ["outlook", "outlook", "OAUTH2", true, "productivity", "Outlook"],
24918
- ["bitbucket", "bitbucket", "OAUTH2", true, "data", "Bitbucket"],
24919
- ["datadog", "datadog", "API_KEY", false, "data", "Datadog"],
24920
- ["pagerduty", "pagerduty", "OAUTH2", true, "data", "PagerDuty"],
24921
- ["intercom", "intercom", "OAUTH2", true, "business", "Intercom"],
24922
- ["zendesk", "zendesk", "OAUTH2", true, "business", "Zendesk"],
24923
- ["hubspot", "hubspot", "OAUTH2", true, "business", "HubSpot"],
24924
- ["supabase", "supabase", "OAUTH2", true, "data", "Supabase"],
24925
- ["figma", "figma", "OAUTH2", true, "productivity", "Figma"],
24926
- ["launchdarkly", "launch_darkly", "API_KEY", false, "data", "LaunchDarkly"],
24927
- ["asana", "asana", "OAUTH2", true, "productivity", "Asana"],
24928
- ["clickup", "clickup", "OAUTH2", true, "productivity", "ClickUp"],
24929
- ["trello", "trello", "OAUTH1", true, "productivity", "Trello"],
24930
- ["todoist", "todoist", "OAUTH2", true, "productivity", "Todoist"],
24931
- ["airtable", "airtable", "OAUTH2", true, "productivity", "Airtable"],
24932
- ["coda", "coda", "API_KEY", false, "productivity", "Coda"],
24933
- ["miro", "miro", "OAUTH2", true, "productivity", "Miro"],
24934
- ["sharepoint", "share_point", "OAUTH2", true, "productivity", "SharePoint"],
24935
- ["onedrive", "one_drive", "OAUTH2", true, "productivity", "OneDrive"],
24936
- ["googleslides", "googleslides", "OAUTH2", true, "productivity", "Google Slides"],
24937
- ["googlemeet", "googlemeet", "OAUTH2", true, "productivity", "Google Meet"],
24938
- ["googletasks", "googletasks", "OAUTH2", true, "productivity", "Google Tasks"],
24939
- ["dropbox", "dropbox", "OAUTH2", true, "productivity", "Dropbox"],
24940
- ["box", "box", "OAUTH2", true, "productivity", "Box"],
24941
- ["discord", "discord", "OAUTH2", true, "productivity", "Discord"],
24942
- ["discordbot", "discordbot", "OAUTH2", true, "productivity", "Discord Bot"],
24943
- ["zoom", "zoom", "OAUTH2", true, "productivity", "Zoom"],
24944
- ["googlechat", "google_chat", "OAUTH2", false, "productivity", "Google Chat"],
24945
- ["newrelic", "new_relic", "API_KEY", false, "data", "New Relic"],
24946
- ["betterstack", "better_stack", "API_KEY", false, "data", "Better Stack"],
24947
- ["incidentio", "incident_io", "API_KEY", false, "data", "incident.io"],
24948
- ["grafana", "grafana", "BEARER_TOKEN", false, "data", "Grafana"],
24949
- ["honeycomb", "honeycomb_mcp", "DCR_OAUTH", false, "data", "Honeycomb MCP"],
24950
- ["bugsnag", "bugsnag", "API_KEY", false, "data", "Bugsnag"],
24951
- ["circleci", "circleci", "API_KEY", false, "data", "CircleCI"],
24952
- ["buildkite", "buildkite", "API_KEY", false, "data", "Buildkite"],
24953
- ["dockerhub", "docker_hub", "API_KEY", false, "data", "Docker Hub"],
24954
- ["digitalocean", "digital_ocean", "OAUTH2", true, "data", "DigitalOcean"],
24955
- ["railway", "railway", "API_KEY", false, "data", "Railway"],
24956
- ["render", "render", "API_KEY", false, "data", "Render"],
24957
- ["firebase", "firebase", "API_KEY", false, "data", "Firebase"],
24958
- ["cloudinary", "cloudinary", "API_KEY", false, "data", "Cloudinary"],
24959
- ["configcat", "configcat", "API_KEY", false, "data", "ConfigCat"],
24960
- ["contextdev", "context_dev", "API_KEY", false, "data", "Context.dev"],
24961
- ["googleanalytics", "google_analytics", "OAUTH2", true, "data", "Google Analytics"],
24962
- ["googlebigquery", "googlebigquery", "OAUTH2", true, "data", "Google BigQuery"],
24963
- ["amplitude", "amplitude", "API_KEY", false, "data", "Amplitude"],
24964
- ["mixpanel", "mixpanel", "BASIC", false, "data", "Mixpanel"],
24965
- ["segment", "segment", "API_KEY", false, "data", "Segment"],
24966
- ["databricks", "databricks", "API_KEY", false, "data", "Databricks"],
24967
- ["snowflake", "snowflake", "OAUTH2", false, "data", "Snowflake"],
24968
- ["algolia", "algolia", "API_KEY", false, "data", "Algolia"],
24969
- ["elasticsearch", "elasticsearch", "API_KEY", false, "data", "Elasticsearch"],
24970
- ["salesforce", "salesforce", "OAUTH2", true, "business", "Salesforce"],
24971
- ["pipedrive", "pipedrive", "API_KEY", false, "business", "Pipedrive"],
24972
- ["close", "close", "API_KEY", false, "business", "Close"],
24973
- ["apollo", "apollo", "API_KEY", false, "business", "Apollo"],
24974
- ["gong", "gong", "OAUTH2", true, "business", "Gong"],
24975
- ["freshdesk", "freshdesk", "API_KEY", false, "business", "Freshdesk"],
24976
- ["helpscout", "help_scout", "OAUTH2", false, "business", "Help Scout"],
24977
- ["servicenow", "servicenow", "BASIC", false, "business", "ServiceNow"],
24978
- ["mailchimp", "mailchimp", "OAUTH2", true, "business", "Mailchimp"],
24979
- ["customerio", "customerio", "API_KEY", false, "business", "Customer.io"],
24980
- ["klaviyo", "klaviyo", "API_KEY", false, "business", "Klaviyo"],
24981
- ["shopify", "shopify", "API_KEY", false, "business", "Shopify"],
24982
- ["quickbooks", "quickbooks", "OAUTH2", true, "business", "QuickBooks"],
24983
- ["xero", "xero", "OAUTH2", false, "business", "Xero"],
24984
- ["brex", "brex", "API_KEY", false, "business", "Brex"],
24985
- ["buffer", "buffer", "OAUTH2", false, "business", "Buffer"],
24986
- ["canva", "canva", "OAUTH2", true, "productivity", "Canva"],
24987
- ["webflow", "webflow", "API_KEY", false, "business", "Webflow"],
24988
- ["firecrawl", "firecrawl", "API_KEY", false, "data", "Firecrawl"],
24989
- ["browserbase", "browserbase_tool", "API_KEY", false, "data", "Browserbase"],
24990
- ["exa", "exa", "API_KEY", false, "data", "Exa"],
24991
- ["youtube", "youtube", "OAUTH2", true, "business", "YouTube"],
24992
- ["twitter", "twitter", "OAUTH2", false, "business", "Twitter/X"],
24993
- ["instagram", "instagram", "OAUTH2", true, "business", "Instagram"],
24994
- ["facebook", "facebook", "OAUTH2", true, "business", "Facebook"]
24995
- ];
24996
- var HOSTED_COMPOSIO_PLUGINS = HOSTED_COMPOSIO_PLUGIN_DEFINITIONS.map(
24997
- ([id, toolkit, authScheme, managedAuth, category, name]) => ({
24998
- id,
24999
- backend: "composio",
25000
- toolkit,
25001
- authType: "hosted",
25002
- authScheme,
25003
- managedAuth,
25004
- category,
25005
- name,
25006
- description: `Use ${name} tools from workspace agents.`
25007
- })
25008
- );
25009
- var PLUGIN_CATALOG = [
25010
- ...EXPLICIT_PLUGIN_CATALOG,
25011
- ...HOSTED_COMPOSIO_PLUGINS
25012
- ];
25013
-
25014
- // ../shared/src/routes/user.ts
25015
- var PROFILE_AVATAR_MAX_SIZE_BYTES = 5 * 1024 * 1024;
25016
-
25017
- // ../shared/src/routes/client-state.ts
25018
- var MAX_CLIENT_STATE_REQUEST_BYTES = 5 * 1024 * 1024;
25019
- var MAX_CLIENT_STATE_BYTES_PER_USER = 10 * 1024 * 1024;
25020
-
25021
- // ../shared/src/routes/workspaces.ts
25022
- var WORKSPACE_STATUSES = ["active", "sleeping", "archived", "preparing", "error"];
25023
- function isWorkspaceSuspendedStatus(status) {
25024
- return status === "sleeping" || status === "archived";
25025
- }
25026
- function workspaceConfigWithCapabilities(config3, capabilities = {}) {
25027
- const mergedCapabilities = {
25028
- ...config3?.capabilities,
25029
- ...capabilities
25030
- };
25031
- return {
25032
- ...config3,
25033
- capabilities: {
25034
- ...mergedCapabilities,
25035
- pr_followups: mergedCapabilities.pr_followups === true
25036
- }
25037
- };
25038
- }
25039
- function workspaceConfigWithPrFollowups(config3, prFollowups = config3?.capabilities?.pr_followups === true) {
25040
- return workspaceConfigWithCapabilities(config3, { pr_followups: prFollowups });
25041
- }
25042
- var WORKSPACE_STATUS_FILTERS = WORKSPACE_STATUSES.map((status) => status === "error" ? "failed" : status);
25043
- var WORKSPACE_SORT_CHOICES = [
25044
- ["manual", "Custom"],
25045
- ["activity", "Last activity"],
25046
- ["created", "Created date"]
25047
- ];
25048
- var WORKSPACE_SORT_OPTIONS = WORKSPACE_SORT_CHOICES.map(([option]) => option);
25049
- var WORKSPACE_SORT_LABELS = Object.fromEntries(WORKSPACE_SORT_CHOICES);
25050
- var WORKSPACE_SIDEBAR_ORGANIZATION_CHOICES = [
25051
- ["chronological", "Chronological"],
25052
- ["environments", "Environments"],
25053
- ["recent", "Recent environments"]
25054
- ];
25055
- var WORKSPACE_SIDEBAR_ORGANIZATIONS = WORKSPACE_SIDEBAR_ORGANIZATION_CHOICES.map(([organization]) => organization);
25056
- var WORKSPACE_SIDEBAR_ORGANIZATION_LABELS = Object.fromEntries(WORKSPACE_SIDEBAR_ORGANIZATION_CHOICES);
25057
- var DEFAULT_STATUS_FILTERS = [...WORKSPACE_STATUS_FILTERS];
25058
- var DEFAULT_WORKSPACE_FILTERS = {
25059
- statuses: [...DEFAULT_STATUS_FILTERS],
25060
- ownerIds: [],
25061
- activity: "any"
25062
- };
25063
- var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
25064
-
25065
- // ../shared/src/routes/workspace-events.ts
25066
- var workspaceChangedEventSchema = external_exports.discriminatedUnion("type", [
25067
- external_exports.object({
25068
- type: external_exports.literal("workspace.changed"),
25069
- workspaceId: external_exports.string(),
25070
- completion: external_exports.object({
25071
- workspaceName: external_exports.string(),
25072
- completedAt: external_exports.string(),
25073
- processing: external_exports.boolean().optional()
25074
- }).optional(),
25075
- ts: external_exports.string()
25076
- }),
25077
- external_exports.object({
25078
- type: external_exports.literal("workspace.chat.changed"),
25079
- workspaceId: external_exports.string(),
25080
- chatId: external_exports.string(),
25081
- ts: external_exports.string()
25082
- }),
25083
- external_exports.object({ type: external_exports.literal("presence.changed"), ts: external_exports.string() }),
25084
- external_exports.object({
25085
- type: external_exports.literal("mobile-testing.changed"),
25086
- workspaceId: external_exports.string(),
25087
- ts: external_exports.string()
25088
- })
25089
- ]);
25090
-
25091
- // ../shared/src/routes/presence.ts
25092
- var presenceStatusSchema = external_exports.enum(["online", "typing"]);
25093
- var presenceLocationSchema = external_exports.object({
25094
- environmentId: external_exports.string().optional(),
25095
- workspaceId: external_exports.string().optional()
25096
- });
25097
- var presenceEntrySchema = external_exports.object({
25098
- userId: external_exports.string(),
25099
- status: presenceStatusSchema,
25100
- location: presenceLocationSchema,
25101
- ts: external_exports.string()
25102
- });
25103
- var updatePresenceRequestSchema = external_exports.object({
25104
- status: presenceStatusSchema,
25105
- location: presenceLocationSchema.optional()
25106
- });
25107
-
25108
- // ../shared/src/audit-log.ts
25109
- var AUDIT_LOG_ACTION = {
25110
- CREATE: "create",
25111
- UPDATE: "update",
25112
- DELETE: "delete",
25113
- CONNECT: "connect",
25114
- DISCONNECT: "disconnect"
25115
- };
25116
- var AUDIT_LOG_ACTIONS = Object.values(AUDIT_LOG_ACTION);
25117
-
25118
- // ../shared/src/automations/types.ts
25119
- var AUTOMATION_DEBOUNCE_MAX_SECONDS = 24 * 60 * 60;
25120
- var AUTOMATION_LIFECYCLE_POLICIES = ["default", "archive_when_done", "sleep_when_done"];
25121
- function toAutomationLifecyclePolicy(policy) {
25122
- if (!policy) return "default";
25123
- if (policy === "default" || policy === "archive_when_done" || policy === "sleep_when_done") return policy;
25124
- return "archive_when_done";
25125
- }
25126
- function lifecyclePolicySupportsAutoStop(policy) {
25127
- return policy === "default";
25128
- }
25129
-
25130
- // ../shared/src/automations/github-checks.ts
25131
- var AUTOMATION_CHECK_CONCLUSIONS = ["success", "failure"];
25132
- function isAutomationCheckConclusion(value) {
25133
- return AUTOMATION_CHECK_CONCLUSIONS.some((conclusion) => conclusion === value);
25134
- }
25135
- function normalizeAutomationGithubCheckNames(names) {
25136
- return names.map((name) => name.trim()).filter(Boolean);
25137
- }
25138
-
25139
- // ../shared/src/automations/agent-validation.ts
25140
- function validateAgentSelection(body, existing, modelCatalogs = {}) {
25141
- const agentProvider = body.agent_provider;
25142
- if (agentProvider !== void 0 && agentProvider !== null) {
25143
- if (!isValidAgentProvider(agentProvider)) {
25144
- return createErrorResult({ message: `Invalid agent_provider: must be one of ${VALID_AGENT_PROVIDERS.join(", ")}` });
25145
- }
25146
- }
25147
- const effectiveAgent = agentProvider !== void 0 ? agentProvider : existing.agentProvider;
25148
- const agentChanged = agentProvider !== void 0 && agentProvider !== existing.agentProvider;
25149
- let model = body.model;
25150
- if (agentChanged && model === void 0 && existing.model) {
25151
- model = null;
25152
- }
25153
- if (model !== void 0 && model !== null) {
25154
- if (typeof model !== "string" || model.trim().length === 0) {
25155
- return createErrorResult({ message: "model must be a non-empty string" });
25156
- }
25157
- model = normalizeClaudeModel(model) ?? model;
25158
- if (!effectiveAgent) {
25159
- return createErrorResult({ message: "Cannot set model without an agent_provider (the model would be applied against the org default agent and may not be valid for it)" });
25160
- }
25161
- const allowed = getAgentModelOptions(effectiveAgent, modelCatalogs);
25162
- if (!isAgentModelAllowed(effectiveAgent, model, modelCatalogs)) {
25163
- return createErrorResult({ message: `Invalid model "${model}" for agent ${effectiveAgent}. Valid models: ${allowed.join(", ")}` });
25164
- }
25165
- }
25166
- const thinkingLevel = body.thinking_level;
25167
- if (thinkingLevel !== void 0 && thinkingLevel !== null) {
25168
- if (!effectiveAgent && isProviderSpecificThinkingLevel(thinkingLevel)) {
25169
- return createErrorResult({ message: `Cannot set ${thinkingLevel} without an agent_provider` });
25170
- }
25171
- if (!isValidThinkingLevel(thinkingLevel) || effectiveAgent && !getThinkingLevelOptions(effectiveAgent).includes(thinkingLevel)) {
25172
- const validLevels = effectiveAgent ? getThinkingLevelOptions(effectiveAgent) : VALID_THINKING_LEVELS;
25173
- return createErrorResult({ message: `Invalid thinking_level: must be one of ${validLevels.join(", ")}` });
25174
- }
25175
- }
25176
- return createSuccessResult({ agentProvider, model, thinkingLevel });
25177
- }
25178
-
25179
- // ../shared/src/automations/github/index.ts
25180
- var REPO_FILTER_PARAM = {
25181
- id: "repository_ids",
25182
- label: "Filter by repository",
25183
- type: "repository_ids"
25184
- };
25185
- var EXCLUDED_ACTORS_PARAM = {
25186
- id: "excluded_actors",
25187
- label: "Exclude users",
25188
- type: "excluded_actors"
25189
- };
25190
- var GROUP_PR_EVENTS_PARAM = {
25191
- id: "group_pr_events",
25192
- label: "Group PR events to same workspace",
25193
- type: "boolean"
25194
- };
25195
- var GITHUB_TRIGGER = {
25196
- type: "github",
25197
- label: "GitHub",
25198
- description: "Trigger on repository events",
25199
- icon: "github",
25200
- events: [
25201
- { id: "pull_request.opened", label: "PR opened", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM, GROUP_PR_EVENTS_PARAM] },
25202
- { id: "pull_request.synchronize", label: "PR updated (new commits)", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM, GROUP_PR_EVENTS_PARAM] },
25203
- { id: "pull_request.merged", label: "PR merged", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM] },
25204
- { id: "pull_request.closed", label: "PR closed", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM] },
25205
- { id: "pull_request.command", label: "PR command (/replicas run)", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM, GROUP_PR_EVENTS_PARAM] }
25206
- ]
25207
- };
25208
-
25209
- // ../shared/src/automations/gitlab/index.ts
25210
- var REPO_FILTER_PARAM2 = {
25211
- id: "repository_ids",
25212
- label: "Filter by project",
25213
- type: "repository_ids"
25214
- };
25215
- var EXCLUDED_ACTORS_PARAM2 = {
25216
- id: "excluded_actors",
25217
- label: "Exclude users",
25218
- type: "excluded_actors"
25219
- };
25220
- var GROUP_MR_EVENTS_PARAM = {
25221
- id: "group_pr_events",
25222
- label: "Group MR events to same workspace",
25223
- type: "boolean"
25224
- };
25225
- var GITLAB_TRIGGER = {
25226
- type: "gitlab",
25227
- label: "GitLab",
25228
- description: "Trigger on project events",
25229
- icon: "gitlab",
25230
- events: [
25231
- { id: "merge_request.opened", label: "MR opened", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2, GROUP_MR_EVENTS_PARAM] },
25232
- { id: "merge_request.updated", label: "MR updated (new commits)", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2, GROUP_MR_EVENTS_PARAM] },
25233
- { id: "merge_request.merged", label: "MR merged", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2] },
25234
- { id: "merge_request.closed", label: "MR closed", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2] }
25235
- ]
25236
- };
25237
-
25238
- // ../shared/src/routes/repo-files.ts
25239
- var MAX_REPO_FILE_CONTENT_BYTES = 256 * 1024;
25240
-
25241
- // ../shared/src/routes/admin.ts
25242
- var WEBHOOK_PROVIDERS = ["linear", "github", "gitlab", "slack", "e2b", "stripe", "sentry"];
25243
- var WEBHOOK_PROVIDER_SET = new Set(WEBHOOK_PROVIDERS);
25244
-
25245
- // ../shared/src/display-message/types.ts
25246
- var BACKGROUND_TASK_SUBTYPES = /* @__PURE__ */ new Set([
25247
- "task_started",
25248
- "task_progress",
25249
- "task_updated",
25250
- "task_notification"
25251
- ]);
25252
- function isBackgroundTaskSubtype(subtype) {
25253
- return typeof subtype === "string" && BACKGROUND_TASK_SUBTYPES.has(subtype);
25254
- }
25255
- function optionalString(value) {
25256
- return typeof value === "string" ? value : void 0;
25257
- }
25258
- function coerceBackgroundTaskPayload(payload) {
25259
- if (!isRecord(payload)) return null;
25260
- const subtype = payload.subtype;
25261
- if (!isBackgroundTaskSubtype(subtype)) return null;
25262
- const taskId = payload.task_id;
25263
- if (typeof taskId !== "string" || !taskId) return null;
25264
- const patch = isRecord(payload.patch) ? {
25265
- status: optionalString(payload.patch.status),
25266
- description: optionalString(payload.patch.description),
25267
- error: optionalString(payload.patch.error),
25268
- isBackgrounded: optionalBoolean(payload.patch.is_backgrounded)
25269
- } : void 0;
25270
- const usage = isRecord(payload.usage) ? {
25271
- totalTokens: optionalNumber(payload.usage.total_tokens),
25272
- toolUses: optionalNumber(payload.usage.tool_uses),
25273
- durationMs: optionalNumber(payload.usage.duration_ms)
25274
- } : void 0;
25275
- return {
25276
- subtype,
25277
- taskId,
25278
- description: optionalString(payload.description),
25279
- summary: optionalString(payload.summary),
25280
- outputFile: optionalString(payload.output_file),
25281
- status: optionalString(payload.status),
25282
- taskType: optionalString(payload.task_type),
25283
- workflowName: optionalString(payload.workflow_name),
25284
- prompt: optionalString(payload.prompt),
25285
- lastToolName: optionalString(payload.last_tool_name),
25286
- usage,
25287
- ambient: optionalBoolean(payload.ambient),
25288
- patch
25289
- };
25290
- }
25291
- function optionalNumber(value) {
25292
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
25293
- }
25294
- function optionalBoolean(value) {
25295
- return typeof value === "boolean" ? value : void 0;
25296
- }
25297
- function normalizeBackgroundTaskStatus(status) {
25298
- if (status === "completed" || status === "failed" || status === "stopped") {
25299
- return status;
25300
- }
25301
- if (status === "killed") {
25302
- return "stopped";
25303
- }
25304
- return "in_progress";
25305
- }
25306
-
25307
- // ../shared/src/display-message/constants.ts
25308
- var USER_MESSAGE_MATCH_GRACE_PERIOD_MS = 3e4;
25309
-
25310
- // ../shared/src/user-message-matching.ts
25311
- function parseTimestampMs(timestamp) {
25312
- const value = Date.parse(timestamp);
25313
- return Number.isFinite(value) ? value : 0;
25314
- }
25315
- function areUserMessagesWithinMatchWindow(a, b) {
25316
- return a.content === b.content && Math.abs(parseTimestampMs(a.timestamp) - parseTimestampMs(b.timestamp)) <= USER_MESSAGE_MATCH_GRACE_PERIOD_MS;
25317
- }
25318
-
25319
- // ../shared/src/agent-event-utils.ts
25320
- function getUserMessage(event) {
25321
- return event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string" ? event.payload.message : null;
25322
- }
25323
- function getUserMessageId(event) {
25324
- const messageId = event.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
25325
- return typeof messageId === "string" ? messageId : null;
25326
- }
25327
-
25328
- // ../shared/src/json.ts
25329
- function safeJsonParse(str, fallback) {
25330
- try {
25331
- return JSON.parse(str);
25332
- } catch {
25333
- return fallback;
25334
- }
25335
- }
25336
-
25337
- // ../shared/src/display-message/parsers/utils.ts
25338
- var INTERRUPTED_MESSAGE_REGEX = /^\[Request interrupted by user.*\]$/;
25339
- function userMessageImages(value) {
25340
- if (!Array.isArray(value)) return void 0;
25341
- const images = value.filter((item) => isRecord(item) && item.type === "image" && typeof item.mediaType === "string" && typeof item.data === "string");
25342
- return images.length > 0 ? images : void 0;
25343
- }
25344
- function stringifyDisplayValue(value) {
25345
- if (value === void 0 || value === null) return void 0;
25346
- if (typeof value === "string") return value;
25347
- try {
25348
- return JSON.stringify(value, null, 2);
25349
- } catch {
25350
- return String(value);
25351
- }
25352
- }
25353
- function upsertDisplayMessage(messages, message) {
25354
- const index = messages.findIndex((candidate) => candidate.id === message.id);
25355
- if (index === -1) messages.push(message);
25356
- else messages[index] = message;
25357
- }
25358
- function applyToolInputEvent(event, messages, provider, state, questionFilter) {
25359
- if (event.type === "replicas-tool-input-request" && typeof event.payload.toolUseId === "string" && typeof event.payload.requestId === "string" && (Array.isArray(event.payload.options) || Array.isArray(event.payload.questions))) {
25360
- const resolved = state.resolutions.get(event.payload.toolUseId);
25361
- const inputRequest = {
25362
- requestId: event.payload.requestId,
25363
- status: resolved?.selectionId === "aborted" || resolved?.selectionId === "declined" ? "aborted" : resolved ? "resolved" : "pending",
25364
- ...Array.isArray(event.payload.options) ? { options: event.payload.options } : {},
25365
- ...Array.isArray(event.payload.questions) ? { questions: questionFilter ? event.payload.questions.filter(questionFilter) : event.payload.questions } : {},
25366
- ...resolved ? { selectionId: resolved.selectionId } : {},
25367
- ...resolved?.selectionSummary ? { selectionSummary: resolved.selectionSummary } : {}
25368
- };
25369
- state.requests.set(event.payload.toolUseId, inputRequest);
25370
- const message = messages.find((candidate) => candidate.type === "tool_call" && candidate.id === `${provider}-tool-${event.payload.toolUseId}`);
25371
- if (message) message.inputRequest = inputRequest;
25372
- return true;
25373
- }
25374
- if (event.type === "replicas-tool-input-resolved" && typeof event.payload.toolUseId === "string" && typeof event.payload.selectionId === "string") {
25375
- const resolved = {
25376
- selectionId: event.payload.selectionId,
25377
- ...typeof event.payload.selectionSummary === "string" ? { selectionSummary: event.payload.selectionSummary } : {}
25378
- };
25379
- state.resolutions.set(event.payload.toolUseId, resolved);
25380
- const inputRequest = state.requests.get(event.payload.toolUseId);
25381
- if (inputRequest) {
25382
- inputRequest.status = resolved.selectionId === "aborted" || resolved.selectionId === "declined" ? "aborted" : "resolved";
25383
- inputRequest.selectionId = resolved.selectionId;
25384
- if (resolved.selectionSummary) inputRequest.selectionSummary = resolved.selectionSummary;
25385
- }
25386
- return true;
25387
- }
25388
- return false;
25389
- }
25390
- function parseSkillName(tool, input) {
25391
- if (typeof tool !== "string") return null;
25392
- const normalizedTool = tool.toLowerCase();
25393
- if (normalizedTool.startsWith("skill.")) return tool.slice("skill.".length) || null;
25394
- if (normalizedTool !== "skill") return null;
25395
- const parsedInput = typeof input === "string" ? safeJsonParse(input, null) : input;
25396
- if (!isRecord(parsedInput)) return null;
25397
- const skillName = parsedInput.skill ?? parsedInput.name;
25398
- return typeof skillName === "string" && skillName ? skillName : null;
25399
- }
25400
- function skillNameFromFilePath(path6) {
25401
- const skillName = skillNameFromSkillRootPath(path6);
25402
- if (skillName) return skillName;
25403
- const parts = path6.split(/[\\/]+/).filter(Boolean);
25404
- if (parts.length < 2 || parts[parts.length - 1].toLowerCase() !== "skill.md") return null;
25405
- const fallbackSkillName = parts[parts.length - 2];
25406
- return fallbackSkillName && fallbackSkillName.toLowerCase() !== "skills" ? fallbackSkillName : null;
25407
- }
25408
- function skillNameFromSkillRootPath(path6) {
25409
- const parts = path6.split(/[\\/]+/).filter(Boolean);
25410
- const skillsIndex = parts.findLastIndex((part) => part.toLowerCase() === "skills");
25411
- if (skillsIndex === -1) return null;
25412
- const firstSkillPart = parts[skillsIndex + 1];
25413
- const skillName = firstSkillPart?.toLowerCase() === ".system" ? parts[skillsIndex + 2] : firstSkillPart;
25414
- return skillName && skillName.toLowerCase() !== "skill.md" ? skillName : null;
25415
- }
25416
- function parseSkillFilePath(input) {
25417
- const parsedInput = typeof input === "string" ? safeJsonParse(input, null) : input;
25418
- if (!isRecord(parsedInput)) return null;
25419
- const path6 = [parsedInput.path, parsedInput.filePath, parsedInput.file_path].find((value) => typeof value === "string" && value.length > 0);
25420
- if (!path6) return null;
25421
- return skillNameFromFilePath(path6);
25422
- }
25423
- function skillNameFromToolCall(tool, input) {
25424
- const directSkillName = parseSkillName(tool, input);
25425
- if (directSkillName) return directSkillName;
25426
- if (typeof tool !== "string" || !["read", "read_file", "readfile"].includes(tool.toLowerCase())) return null;
25427
- return parseSkillFilePath(input);
25428
- }
25429
- function createCallDisplayMessage(message) {
25430
- const skillName = message.server.toLowerCase() === "skills" ? message.tool : skillNameFromToolCall(message.tool, message.input);
25431
- if (skillName === null) return { ...message, type: "tool_call" };
25432
- return {
25433
- id: message.id,
25434
- type: "skill",
25435
- skillName,
25436
- status: message.status,
25437
- timestamp: message.timestamp
25438
- };
25439
- }
25440
- function insertDisplayMessageByTimestamp(messages, message) {
25441
- const messageMs = parseTimestampMs(message.timestamp);
25442
- let index = messages.length;
25443
- while (index > 0 && parseTimestampMs(messages[index - 1].timestamp) > messageMs) index--;
25444
- messages.splice(index, 0, message);
25445
- }
25446
-
25447
- // ../shared/src/display-message/format.ts
25448
- function unquoteShellArg(value) {
25449
- const trimmed = value.trim();
25450
- if (trimmed.length < 2) return trimmed;
25451
- if (trimmed[0] === "'" && trimmed[trimmed.length - 1] === "'") {
25452
- return trimmed.slice(1, -1).replace(/'\\''/g, "'");
25453
- }
25454
- if (trimmed[0] === '"' && trimmed[trimmed.length - 1] === '"') {
25455
- return trimmed.slice(1, -1).replace(/\\(["\\$`])/g, "$1");
25456
- }
25457
- return trimmed;
25458
- }
25459
- function shellPayload(command) {
25460
- const match = command.trim().match(/^(?:\S*\/)?(?:ba|z|c|fi)?sh\s+-(?:[a-zA-Z]*c[a-zA-Z]*|c)\s+([\s\S]+)$/);
25461
- return match ? unquoteShellArg(match[1]) : command.trim();
25462
- }
25463
- function splitShellWords(command) {
25464
- const words = [];
25465
- let word = "";
25466
- let quote = null;
25467
- let escaped = false;
25468
- for (const char of command) {
25469
- if (escaped) {
25470
- word += char;
25471
- escaped = false;
25472
- continue;
25473
- }
25474
- if (char === "\\" && quote !== "'") {
25475
- escaped = true;
25476
- continue;
25477
- }
25478
- if (quote) {
25479
- if (char === quote) quote = null;
25480
- else word += char;
25481
- continue;
25482
- }
25483
- if (char === '"' || char === "'") {
25484
- quote = char;
25485
- continue;
25486
- }
25487
- if (/\s/.test(char)) {
25488
- if (word) {
25489
- words.push(word);
25490
- word = "";
25491
- }
25492
- continue;
25493
- }
25494
- word += char;
25495
- }
25496
- if (word) words.push(word);
25497
- return words;
25498
- }
25499
- function commandWords(command) {
25500
- const payload = shellPayload(command);
25501
- if (!payload || /[;|<>]|&&/.test(payload)) return null;
25502
- return splitShellWords(payload);
25503
- }
25504
- function skillNamesFromCommand(command) {
25505
- const words = commandWords(command);
25506
- if (!words) return [];
25507
- return Array.from(new Set(words.flatMap((word) => {
25508
- const skillName = skillNameFromSkillRootPath(word);
25509
- return skillName ? [skillName] : [];
25510
- })));
25511
- }
25512
-
25513
- // ../shared/src/display-message/parsers/codex-parser.ts
25514
- function getStatusFromExitCode(exitCode) {
25515
- return exitCode === 0 ? "completed" : "failed";
25516
- }
25517
- function getPayloadString(event, key) {
25518
- const value = event.payload?.[key];
25519
- return typeof value === "string" ? value : null;
25520
- }
25521
- function displayId(event, eventIndex, prefix) {
25522
- const stableId = getPayloadString(event, USER_MESSAGE_ID_PAYLOAD_KEY) ?? getPayloadString(event, CODEX_ASP_ITEM_ID_PAYLOAD_KEY);
25523
- return stableId ? `${prefix}-${stableId}` : `${prefix}-${event.timestamp}-${eventIndex}`;
25524
- }
25525
- function parseShellOutput(raw) {
25526
- const exitCodeMatch = raw.match(/^Exit code: (\d+)/m) || raw.match(/Process exited with code (\d+)/);
25527
- const exitCode = exitCodeMatch ? parseInt(exitCodeMatch[1], 10) : 0;
25528
- const outputIndex = raw.indexOf("Output:\n");
25529
- const output = outputIndex >= 0 ? raw.slice(outputIndex + "Output:\n".length) : raw;
25530
- return { exitCode, output };
24729
+ function parseShellOutput(raw) {
24730
+ const exitCodeMatch = raw.match(/^Exit code: (\d+)/m) || raw.match(/Process exited with code (\d+)/);
24731
+ const exitCode = exitCodeMatch ? parseInt(exitCodeMatch[1], 10) : 0;
24732
+ const outputIndex = raw.indexOf("Output:\n");
24733
+ const output = outputIndex >= 0 ? raw.slice(outputIndex + "Output:\n".length) : raw;
24734
+ return { exitCode, output };
25531
24735
  }
25532
24736
  function parsePatch(input) {
25533
24737
  const operations = [];
@@ -26204,6 +25408,44 @@ function parseFxEvents(events) {
26204
25408
  return parseAcpEvents(events, "fx");
26205
25409
  }
26206
25410
 
25411
+ // ../shared/src/errors.ts
25412
+ var TRANSIENT_NETWORK_ERROR_PATTERNS = [
25413
+ /socket connection was closed unexpectedly/,
25414
+ /fetch failed/,
25415
+ /network error/,
25416
+ /network request failed/,
25417
+ /econnreset/,
25418
+ /econnrefused/,
25419
+ /etimedout/,
25420
+ /enotfound/,
25421
+ /eai_again/,
25422
+ /und_err_socket/,
25423
+ /und_err_connect_timeout/,
25424
+ /headers timeout/,
25425
+ /body timeout/,
25426
+ /connection reset/,
25427
+ /connection closed/,
25428
+ /closed unexpectedly/
25429
+ ];
25430
+ var HTTP_CONTEXT_PATTERN = /(api error|request|response|status|http)/;
25431
+ var HTTP_STATUS_PATTERN = /(^|[^0-9])([1-5][0-9]{2})(?=$|[^0-9])/g;
25432
+ function isTransientErrorText(text, options = {}) {
25433
+ const normalized = text.toLowerCase();
25434
+ if (!normalized) return false;
25435
+ if (TRANSIENT_NETWORK_ERROR_PATTERNS.some((pattern) => pattern.test(normalized))) return true;
25436
+ if (options.includeRateLimit && /rate limit/.test(normalized)) return true;
25437
+ if (options.extraPatterns?.some((pattern) => pattern.test(normalized))) return true;
25438
+ const httpStatuses = new Set(options.httpStatuses ?? []);
25439
+ const httpStatusClasses = new Set(options.httpStatusClasses ?? []);
25440
+ if (httpStatuses.size === 0 && httpStatusClasses.size === 0) return false;
25441
+ if (options.requireHttpContext !== false && !HTTP_CONTEXT_PATTERN.test(normalized)) return false;
25442
+ for (const match of normalized.matchAll(HTTP_STATUS_PATTERN)) {
25443
+ const status = Number(match[2]);
25444
+ if (httpStatuses.has(status) || httpStatusClasses.has(Math.floor(status / 100))) return true;
25445
+ }
25446
+ return false;
25447
+ }
25448
+
26207
25449
  // ../shared/src/display-message/parsers/inference-error.ts
26208
25450
  var TRANSIENT_HTTP_STATUSES = [408, 429, 500, 502, 503, 504, 529];
26209
25451
  var SERVER_ERROR_TEXT_PATTERN = /internal ?server ?error|server ?error|service unavailable|bad gateway|gateway timeout|overloaded|too many requests/i;
@@ -26694,6 +25936,15 @@ function parseMcpToolName(name) {
26694
25936
  return { server, tool };
26695
25937
  }
26696
25938
 
25939
+ // ../shared/src/user-message-matching.ts
25940
+ function parseTimestampMs(timestamp) {
25941
+ const value = Date.parse(timestamp);
25942
+ return Number.isFinite(value) ? value : 0;
25943
+ }
25944
+ function areUserMessagesWithinMatchWindow(a, b) {
25945
+ return a.content === b.content && Math.abs(parseTimestampMs(a.timestamp) - parseTimestampMs(b.timestamp)) <= USER_MESSAGE_MATCH_GRACE_PERIOD_MS;
25946
+ }
25947
+
26697
25948
  // ../shared/src/display-message/parsers/claude-parser.ts
26698
25949
  var ABORTED_TERMINAL_REASONS = /* @__PURE__ */ new Set(["aborted_streaming", "aborted_tools"]);
26699
25950
  function coerceClaudeResultPayload(payload) {
@@ -27060,577 +26311,1368 @@ function parseClaudeEvents(events, parentToolUseId) {
27060
26311
  timestamp: event.timestamp
27061
26312
  });
27062
26313
  } else {
27063
- messages.push({
27064
- id: `toolcall-${event.timestamp}-${messages.length}`,
27065
- type: "tool_call",
27066
- server: "claude",
27067
- tool: toolName,
27068
- input: toolInput,
27069
- status: "in_progress",
27070
- timestamp: event.timestamp
27071
- });
26314
+ messages.push({
26315
+ id: `toolcall-${event.timestamp}-${messages.length}`,
26316
+ type: "tool_call",
26317
+ server: "claude",
26318
+ tool: toolName,
26319
+ input: toolInput,
26320
+ status: "in_progress",
26321
+ timestamp: event.timestamp
26322
+ });
26323
+ }
26324
+ }
26325
+ });
26326
+ }
26327
+ if (event.type === "claude-result") {
26328
+ const payload = coerceClaudeResultPayload(event.payload);
26329
+ if (isClaudeResultError(payload)) {
26330
+ const genuineErrors = stripAgentDiagnosticErrors(payload.errors ?? []);
26331
+ messages.push({
26332
+ id: `error-${event.timestamp}`,
26333
+ type: "error",
26334
+ message: genuineErrors.length > 0 ? genuineErrors.join("\n") : "Claude session encountered an unexpected error.",
26335
+ timestamp: event.timestamp
26336
+ });
26337
+ }
26338
+ }
26339
+ if (event.type === "claude-system") {
26340
+ const payload = coerceBackgroundTaskPayload(event.payload);
26341
+ if (payload?.ambient) ambientTaskIds.add(payload.taskId);
26342
+ if (payload && !ambientTaskIds.has(payload.taskId)) {
26343
+ const existing = taskMessageMap.get(payload.taskId);
26344
+ const nextStatus = payload.subtype === "task_notification" ? normalizeBackgroundTaskStatus(payload.status) : payload.subtype === "task_updated" ? normalizeBackgroundTaskStatus(payload.patch?.status) : existing?.status ?? "in_progress";
26345
+ const nextMessage = {
26346
+ id: existing?.id ?? `task-${event.timestamp}-${messages.length}`,
26347
+ type: "background_task",
26348
+ taskId: payload.taskId,
26349
+ status: nextStatus,
26350
+ description: payload.description ?? payload.patch?.description ?? existing?.description,
26351
+ summary: payload.summary ?? existing?.summary,
26352
+ outputFile: payload.outputFile ?? existing?.outputFile,
26353
+ taskType: payload.taskType ?? existing?.taskType,
26354
+ workflowName: payload.workflowName ?? existing?.workflowName,
26355
+ prompt: payload.prompt ?? existing?.prompt,
26356
+ lastToolName: payload.lastToolName ?? existing?.lastToolName,
26357
+ usage: payload.usage ?? existing?.usage,
26358
+ error: payload.patch?.error ?? existing?.error,
26359
+ isBackgrounded: payload.patch?.isBackgrounded ?? existing?.isBackgrounded,
26360
+ timestamp: existing?.timestamp ?? event.timestamp
26361
+ };
26362
+ if (existing) {
26363
+ Object.assign(existing, nextMessage);
26364
+ } else {
26365
+ taskMessageMap.set(payload.taskId, nextMessage);
26366
+ messages.push(nextMessage);
26367
+ }
26368
+ }
26369
+ }
26370
+ });
26371
+ filteredEvents.forEach((event) => {
26372
+ if (event.type === "replicas-tool-input-request") {
26373
+ const payload = event.payload;
26374
+ if (!payload.toolUseId || !payload.requestId) return;
26375
+ if (!Array.isArray(payload.options) && !Array.isArray(payload.questions)) return;
26376
+ const toolInfo = toolCallMap.get(payload.toolUseId);
26377
+ if (!toolInfo) return;
26378
+ const message = messages[toolInfo.messageIndex];
26379
+ if (!message || message.type !== "tool_call") return;
26380
+ message.inputRequest = {
26381
+ requestId: payload.requestId,
26382
+ status: "pending",
26383
+ options: payload.options,
26384
+ questions: payload.questions
26385
+ };
26386
+ }
26387
+ if (event.type === "replicas-tool-input-resolved") {
26388
+ const payload = event.payload;
26389
+ if (!payload.toolUseId || !payload.selectionId) return;
26390
+ const toolInfo = toolCallMap.get(payload.toolUseId);
26391
+ if (!toolInfo) return;
26392
+ const message = messages[toolInfo.messageIndex];
26393
+ if (!message || message.type !== "tool_call" || !message.inputRequest) return;
26394
+ message.inputRequest = {
26395
+ ...message.inputRequest,
26396
+ status: payload.selectionId === "aborted" ? "aborted" : "resolved",
26397
+ selectionId: payload.selectionId,
26398
+ selectionSummary: payload.selectionSummary
26399
+ };
26400
+ }
26401
+ });
26402
+ filteredEvents.forEach((event) => {
26403
+ if (event.type === "claude-user") {
26404
+ const content = normalizeContentBlocks(event.payload.message?.content);
26405
+ const toolResult = content.find((c) => c.type === "tool_result");
26406
+ if (toolResult && toolResult.tool_use_id) {
26407
+ const toolInfo = toolCallMap.get(toolResult.tool_use_id);
26408
+ if (!toolInfo) return;
26409
+ const resultContent = extractToolResultText(toolResult.content);
26410
+ const isError = toolResult.is_error || false;
26411
+ const status = isError ? "failed" : "completed";
26412
+ if (toolInfo.toolName === "TaskCreate" && !isError) {
26413
+ taskAccumulator.processContentBlock(toolResult);
26414
+ }
26415
+ const message = messages[toolInfo.messageIndex];
26416
+ if (!message) return;
26417
+ if (message.type === "command") {
26418
+ message.output = resultContent;
26419
+ message.status = status;
26420
+ const exitCodeMatch = resultContent.match(/exit code:?\s*(\d+)/i);
26421
+ if (exitCodeMatch) {
26422
+ message.exitCode = parseInt(exitCodeMatch[1], 10);
26423
+ } else {
26424
+ message.exitCode = isError ? 1 : 0;
26425
+ }
26426
+ } else if (message.type === "patch") {
26427
+ message.status = status;
26428
+ } else if (message.type === "web_search") {
26429
+ message.status = status;
26430
+ } else if (message.type === "tool_call") {
26431
+ message.output = resultContent;
26432
+ message.status = status;
26433
+ } else if (message.type === "skill") {
26434
+ message.status = status;
26435
+ } else if (message.type === "subagent") {
26436
+ message.output = resultContent;
26437
+ message.status = status;
26438
+ if (!message.chatId && resultContent) {
26439
+ const parsed = safeJsonParse(resultContent, {});
26440
+ if (parsed.chatId) {
26441
+ message.chatId = parsed.chatId;
26442
+ }
26443
+ }
26444
+ } else if (message.type === "subagent_followup") {
26445
+ message.output = resultContent;
26446
+ message.status = status;
26447
+ }
26448
+ }
26449
+ }
26450
+ });
26451
+ for (const message of messages) {
26452
+ if (message.type !== "subagent_followup") continue;
26453
+ const target = messages.find((candidate) => candidate.type === "subagent" && candidate.chatId === message.chatId);
26454
+ if (target?.type === "subagent") {
26455
+ message.targetTitle = target.description;
26456
+ message.targetProvider = target.subagentType;
26457
+ }
26458
+ }
26459
+ const staleIndexes = new Set(supersededUserMessageIndexes);
26460
+ for (const streamId of [...completedStreamIds, ...supersededStreamIds]) {
26461
+ const refs = partialIndexes.get(streamId);
26462
+ if (refs?.reasoning !== void 0) staleIndexes.add(refs.reasoning);
26463
+ if (refs?.agent !== void 0) staleIndexes.add(refs.agent);
26464
+ }
26465
+ if (staleIndexes.size === 0) return messages;
26466
+ return messages.filter((_2, index) => !staleIndexes.has(index));
26467
+ }
26468
+
26469
+ // ../shared/src/display-message/parsers/codex-asp-parser.ts
26470
+ var DUPLICATE_WINDOW_MS = 5 * 60 * 1e3;
26471
+ function nearTimestamp(a, b) {
26472
+ return Math.abs(parseTimestampMs(a) - parseTimestampMs(b)) <= DUPLICATE_WINDOW_MS;
26473
+ }
26474
+ function inputForDisplay(input) {
26475
+ if (input === void 0 || input === null) return void 0;
26476
+ if (typeof input === "string") return input;
26477
+ if (isRecord(input)) return input;
26478
+ return String(input);
26479
+ }
26480
+ function isInternalUserMessage(content) {
26481
+ const trimmed = content.trim();
26482
+ return trimmed.startsWith("<environment_context>") || trimmed.startsWith("<goal_context>");
26483
+ }
26484
+ function messageForItem(item) {
26485
+ if (item.type === "userMessage") {
26486
+ if (isInternalUserMessage(item.content)) return null;
26487
+ return {
26488
+ id: `user-${item.id}`,
26489
+ type: "user",
26490
+ content: item.content,
26491
+ images: item.images,
26492
+ timestamp: item.timestamp
26493
+ };
26494
+ }
26495
+ if (item.type === "agentMessage") {
26496
+ if (!item.text) return null;
26497
+ return {
26498
+ id: `agent-${item.id}`,
26499
+ type: "agent",
26500
+ content: item.text,
26501
+ timestamp: item.timestamp
26502
+ };
26503
+ }
26504
+ if (item.type === "reasoning") {
26505
+ if (!item.text) return null;
26506
+ return {
26507
+ id: `reasoning-${item.id}`,
26508
+ type: "reasoning",
26509
+ content: item.text,
26510
+ status: normalizeCodexAspTranscriptStatus(item.status),
26511
+ timestamp: item.timestamp,
26512
+ ...item.sourceTimestamp ? { sourceTimestamp: item.sourceTimestamp } : {}
26513
+ };
26514
+ }
26515
+ if (item.type === "commandExecution") {
26516
+ const skillNames = skillNamesFromCommand(item.command);
26517
+ return {
26518
+ id: `command-${item.id}`,
26519
+ type: "command",
26520
+ command: item.command,
26521
+ ...skillNames.length > 0 ? { skillNames } : {},
26522
+ output: item.output,
26523
+ exitCode: item.exitCode ?? void 0,
26524
+ status: normalizeCodexAspTranscriptStatus(item.status),
26525
+ timestamp: item.timestamp
26526
+ };
26527
+ }
26528
+ if (item.type === "fileChange") {
26529
+ return {
26530
+ id: `patch-${item.id}`,
26531
+ type: "patch",
26532
+ operations: item.operations,
26533
+ output: item.output,
26534
+ exitCode: item.exitCode ?? void 0,
26535
+ status: normalizeCodexAspTranscriptStatus(item.status),
26536
+ timestamp: item.timestamp
26537
+ };
26538
+ }
26539
+ if (item.type === "toolCall") {
26540
+ return createCallDisplayMessage({
26541
+ id: `toolcall-${item.id}`,
26542
+ server: item.server,
26543
+ tool: item.tool,
26544
+ input: inputForDisplay(item.input),
26545
+ output: item.output,
26546
+ status: normalizeCodexAspTranscriptStatus(item.status),
26547
+ timestamp: item.timestamp
26548
+ });
26549
+ }
26550
+ if (item.type === "subagent") {
26551
+ return {
26552
+ id: `subagent-${item.id}`,
26553
+ type: "subagent",
26554
+ toolUseId: item.id,
26555
+ description: item.description,
26556
+ prompt: item.prompt,
26557
+ subagentType: item.subagentType,
26558
+ ...item.receiverThreadIds ? { receiverThreadIds: item.receiverThreadIds } : {},
26559
+ ...item.model ? { model: item.model } : {},
26560
+ status: normalizeCodexAspTranscriptStatus(item.status),
26561
+ ...item.output ? { output: item.output } : {},
26562
+ nestedEvents: [],
26563
+ timestamp: item.timestamp
26564
+ };
26565
+ }
26566
+ if (item.type === "webSearch") {
26567
+ return {
26568
+ id: `web-search-${item.id}`,
26569
+ type: "web_search",
26570
+ query: item.query,
26571
+ status: normalizeCodexAspTranscriptStatus(item.status),
26572
+ timestamp: item.timestamp
26573
+ };
26574
+ }
26575
+ if (item.type === "plan") {
26576
+ if (!item.text) return null;
26577
+ return {
26578
+ id: `todo-${item.id}`,
26579
+ type: "todo_list",
26580
+ items: item.text.split("\n").map((line) => line.trim()).filter(Boolean).map((text) => ({ text, completed: item.status === "completed" })),
26581
+ status: normalizeCodexAspTranscriptStatus(item.status),
26582
+ timestamp: item.timestamp
26583
+ };
26584
+ }
26585
+ if (item.type === "contextCompaction") {
26586
+ return {
26587
+ id: `reasoning-${item.id}`,
26588
+ type: "reasoning",
26589
+ content: "Context compacted",
26590
+ status: normalizeCodexAspTranscriptStatus(item.status),
26591
+ timestamp: item.timestamp
26592
+ };
26593
+ }
26594
+ return {
26595
+ id: `error-${item.id}`,
26596
+ type: "error",
26597
+ message: item.message,
26598
+ timestamp: item.timestamp
26599
+ };
26600
+ }
26601
+ function skillMessagesForItem(item) {
26602
+ if (item.type !== "userMessage" || !item.skillNames) return [];
26603
+ return item.skillNames.filter((skillName) => skillName.trim().length > 0).map((skillName, index) => ({
26604
+ id: `skill-${item.id}-${index}`,
26605
+ type: "skill",
26606
+ skillName,
26607
+ status: "completed",
26608
+ timestamp: item.timestamp
26609
+ }));
26610
+ }
26611
+ function stableString(value) {
26612
+ if (value === void 0) return "";
26613
+ if (typeof value !== "object" || value === null) return String(value);
26614
+ try {
26615
+ return JSON.stringify(value, Object.keys(value).sort());
26616
+ } catch {
26617
+ return String(value);
26618
+ }
26619
+ }
26620
+ function duplicateKey(message) {
26621
+ if (message.type === "user" || message.type === "agent" || message.type === "reasoning") {
26622
+ return JSON.stringify([message.type, message.content]);
26623
+ }
26624
+ if (message.type === "command") return JSON.stringify([message.type, message.command]);
26625
+ if (message.type === "web_search") return JSON.stringify([message.type, message.query]);
26626
+ if (message.type === "subagent") {
26627
+ return JSON.stringify([
26628
+ message.type,
26629
+ message.description,
26630
+ message.prompt,
26631
+ message.subagentType,
26632
+ message.model
26633
+ ]);
26634
+ }
26635
+ if (message.type === "todo_list") {
26636
+ return JSON.stringify([message.type, stableString(message.items)]);
26637
+ }
26638
+ if (message.type === "patch") {
26639
+ return JSON.stringify([message.type, stableString(message.operations)]);
26640
+ }
26641
+ if (message.type === "tool_call") {
26642
+ return JSON.stringify([
26643
+ message.type,
26644
+ message.server,
26645
+ message.tool,
26646
+ stableString(message.input)
26647
+ ]);
26648
+ }
26649
+ return null;
26650
+ }
26651
+ function duplicateBucketKey(message, bucketOffset = 0) {
26652
+ const key = duplicateKey(message);
26653
+ if (key === null) return null;
26654
+ const bucket = Math.floor(parseTimestampMs(message.timestamp) / DUPLICATE_WINDOW_MS) + bucketOffset;
26655
+ return JSON.stringify([bucket, key]);
26656
+ }
26657
+ function mergeCodexAspDisplayMessages(primary, supplemental) {
26658
+ const merged = [...primary];
26659
+ const primaryCount = primary.length;
26660
+ const firstIndexById = /* @__PURE__ */ new Map();
26661
+ const indexesByDuplicateBucket = /* @__PURE__ */ new Map();
26662
+ const indexMessage = (message, index) => {
26663
+ if (!firstIndexById.has(message.id)) firstIndexById.set(message.id, index);
26664
+ const key = duplicateBucketKey(message);
26665
+ if (key === null) return;
26666
+ const indexes = indexesByDuplicateBucket.get(key) ?? /* @__PURE__ */ new Set();
26667
+ indexes.add(index);
26668
+ indexesByDuplicateBucket.set(key, indexes);
26669
+ };
26670
+ const unindexMessage = (message, index) => {
26671
+ const key = duplicateBucketKey(message);
26672
+ if (key === null) return;
26673
+ const indexes = indexesByDuplicateBucket.get(key);
26674
+ indexes?.delete(index);
26675
+ if (indexes?.size === 0) indexesByDuplicateBucket.delete(key);
26676
+ };
26677
+ merged.forEach(indexMessage);
26678
+ for (const message of supplemental) {
26679
+ let duplicateIndex = firstIndexById.get(message.id);
26680
+ if (duplicateIndex === void 0) {
26681
+ for (let bucketOffset = -1; bucketOffset <= 1; bucketOffset += 1) {
26682
+ const key = duplicateBucketKey(message, bucketOffset);
26683
+ for (const index of (key === null ? void 0 : indexesByDuplicateBucket.get(key)) ?? []) {
26684
+ if (nearTimestamp(merged[index].timestamp, message.timestamp) && (duplicateIndex === void 0 || index < duplicateIndex)) {
26685
+ duplicateIndex = index;
27072
26686
  }
27073
26687
  }
27074
- });
27075
- }
27076
- if (event.type === "claude-result") {
27077
- const payload = coerceClaudeResultPayload(event.payload);
27078
- if (isClaudeResultError(payload)) {
27079
- const genuineErrors = stripAgentDiagnosticErrors(payload.errors ?? []);
27080
- messages.push({
27081
- id: `error-${event.timestamp}`,
27082
- type: "error",
27083
- message: genuineErrors.length > 0 ? genuineErrors.join("\n") : "Claude session encountered an unexpected error.",
27084
- timestamp: event.timestamp
27085
- });
27086
- }
27087
- }
27088
- if (event.type === "claude-system") {
27089
- const payload = coerceBackgroundTaskPayload(event.payload);
27090
- if (payload?.ambient) ambientTaskIds.add(payload.taskId);
27091
- if (payload && !ambientTaskIds.has(payload.taskId)) {
27092
- const existing = taskMessageMap.get(payload.taskId);
27093
- const nextStatus = payload.subtype === "task_notification" ? normalizeBackgroundTaskStatus(payload.status) : payload.subtype === "task_updated" ? normalizeBackgroundTaskStatus(payload.patch?.status) : existing?.status ?? "in_progress";
27094
- const nextMessage = {
27095
- id: existing?.id ?? `task-${event.timestamp}-${messages.length}`,
27096
- type: "background_task",
27097
- taskId: payload.taskId,
27098
- status: nextStatus,
27099
- description: payload.description ?? payload.patch?.description ?? existing?.description,
27100
- summary: payload.summary ?? existing?.summary,
27101
- outputFile: payload.outputFile ?? existing?.outputFile,
27102
- taskType: payload.taskType ?? existing?.taskType,
27103
- workflowName: payload.workflowName ?? existing?.workflowName,
27104
- prompt: payload.prompt ?? existing?.prompt,
27105
- lastToolName: payload.lastToolName ?? existing?.lastToolName,
27106
- usage: payload.usage ?? existing?.usage,
27107
- error: payload.patch?.error ?? existing?.error,
27108
- isBackgrounded: payload.patch?.isBackgrounded ?? existing?.isBackgrounded,
27109
- timestamp: existing?.timestamp ?? event.timestamp
27110
- };
27111
- if (existing) {
27112
- Object.assign(existing, nextMessage);
27113
- } else {
27114
- taskMessageMap.set(payload.taskId, nextMessage);
27115
- messages.push(nextMessage);
27116
- }
27117
26688
  }
27118
26689
  }
27119
- });
27120
- filteredEvents.forEach((event) => {
27121
- if (event.type === "replicas-tool-input-request") {
27122
- const payload = event.payload;
27123
- if (!payload.toolUseId || !payload.requestId) return;
27124
- if (!Array.isArray(payload.options) && !Array.isArray(payload.questions)) return;
27125
- const toolInfo = toolCallMap.get(payload.toolUseId);
27126
- if (!toolInfo) return;
27127
- const message = messages[toolInfo.messageIndex];
27128
- if (!message || message.type !== "tool_call") return;
27129
- message.inputRequest = {
27130
- requestId: payload.requestId,
27131
- status: "pending",
27132
- options: payload.options,
27133
- questions: payload.questions
27134
- };
27135
- }
27136
- if (event.type === "replicas-tool-input-resolved") {
27137
- const payload = event.payload;
27138
- if (!payload.toolUseId || !payload.selectionId) return;
27139
- const toolInfo = toolCallMap.get(payload.toolUseId);
27140
- if (!toolInfo) return;
27141
- const message = messages[toolInfo.messageIndex];
27142
- if (!message || message.type !== "tool_call" || !message.inputRequest) return;
27143
- message.inputRequest = {
27144
- ...message.inputRequest,
27145
- status: payload.selectionId === "aborted" ? "aborted" : "resolved",
27146
- selectionId: payload.selectionId,
27147
- selectionSummary: payload.selectionSummary
26690
+ if (duplicateIndex === void 0) {
26691
+ indexMessage(message, merged.push(message) - 1);
26692
+ } else if (message.type === "user") {
26693
+ unindexMessage(merged[duplicateIndex], duplicateIndex);
26694
+ merged[duplicateIndex] = {
26695
+ ...merged[duplicateIndex],
26696
+ id: message.id
27148
26697
  };
26698
+ firstIndexById.set(message.id, duplicateIndex);
26699
+ indexMessage(merged[duplicateIndex], duplicateIndex);
27149
26700
  }
26701
+ }
26702
+ const ordered = merged.slice(0, primaryCount);
26703
+ const supplementalOnly = merged.slice(primaryCount).map((message, index) => ({ message, index })).sort((a, b) => parseTimestampMs(a.message.timestamp) - parseTimestampMs(b.message.timestamp) || a.index - b.index);
26704
+ for (const { message } of supplementalOnly) {
26705
+ insertDisplayMessageByTimestamp(ordered, message);
26706
+ }
26707
+ return ordered;
26708
+ }
26709
+ function parseCodexAspTranscript(transcript) {
26710
+ const messages = [];
26711
+ const orderedItems = transcript.turns.map((turn, turnIndex) => ({ turn, turnIndex })).flatMap(({ turn, turnIndex }) => turn.items.map((item, itemIndex) => ({ item, turn, turnIndex, itemIndex }))).sort((a, b) => {
26712
+ const aSequence = a.item.sequence ?? Number.MAX_SAFE_INTEGER;
26713
+ const bSequence = b.item.sequence ?? Number.MAX_SAFE_INTEGER;
26714
+ return aSequence - bSequence || parseTimestampMs(a.item.timestamp) - parseTimestampMs(b.item.timestamp) || a.turnIndex - b.turnIndex || a.itemIndex - b.itemIndex;
27150
26715
  });
27151
- filteredEvents.forEach((event) => {
27152
- if (event.type === "claude-user") {
27153
- const content = normalizeContentBlocks(event.payload.message?.content);
27154
- const toolResult = content.find((c) => c.type === "tool_result");
27155
- if (toolResult && toolResult.tool_use_id) {
27156
- const toolInfo = toolCallMap.get(toolResult.tool_use_id);
27157
- if (!toolInfo) return;
27158
- const resultContent = extractToolResultText(toolResult.content);
27159
- const isError = toolResult.is_error || false;
27160
- const status = isError ? "failed" : "completed";
27161
- if (toolInfo.toolName === "TaskCreate" && !isError) {
27162
- taskAccumulator.processContentBlock(toolResult);
27163
- }
27164
- const message = messages[toolInfo.messageIndex];
27165
- if (!message) return;
27166
- if (message.type === "command") {
27167
- message.output = resultContent;
27168
- message.status = status;
27169
- const exitCodeMatch = resultContent.match(/exit code:?\s*(\d+)/i);
27170
- if (exitCodeMatch) {
27171
- message.exitCode = parseInt(exitCodeMatch[1], 10);
27172
- } else {
27173
- message.exitCode = isError ? 1 : 0;
27174
- }
27175
- } else if (message.type === "patch") {
27176
- message.status = status;
27177
- } else if (message.type === "web_search") {
27178
- message.status = status;
27179
- } else if (message.type === "tool_call") {
27180
- message.output = resultContent;
27181
- message.status = status;
27182
- } else if (message.type === "skill") {
27183
- message.status = status;
27184
- } else if (message.type === "subagent") {
27185
- message.output = resultContent;
27186
- message.status = status;
27187
- if (!message.chatId && resultContent) {
27188
- const parsed = safeJsonParse(resultContent, {});
27189
- if (parsed.chatId) {
27190
- message.chatId = parsed.chatId;
27191
- }
27192
- }
27193
- } else if (message.type === "subagent_followup") {
27194
- message.output = resultContent;
27195
- message.status = status;
27196
- }
26716
+ const reasoningByTurnId = /* @__PURE__ */ new Map();
26717
+ for (const { item, turn } of orderedItems) {
26718
+ if (item.type !== "reasoning" || !item.text.trim()) continue;
26719
+ const reasoning = reasoningByTurnId.get(turn.id) ?? [];
26720
+ reasoning.push(item);
26721
+ reasoningByTurnId.set(turn.id, reasoning);
26722
+ }
26723
+ const emittedReasoningTurnIds = /* @__PURE__ */ new Set();
26724
+ const coalescedItems = orderedItems.flatMap((entry) => {
26725
+ if (entry.item.type !== "reasoning") return [entry];
26726
+ const reasoning = reasoningByTurnId.get(entry.turn.id);
26727
+ if (!reasoning || emittedReasoningTurnIds.has(entry.turn.id)) return [];
26728
+ emittedReasoningTurnIds.add(entry.turn.id);
26729
+ return [{
26730
+ ...entry,
26731
+ item: {
26732
+ type: "reasoning",
26733
+ id: entry.turn.id,
26734
+ text: reasoning.map(({ text }) => text.trim()).join("\n\n"),
26735
+ timestamp: entry.turn.startedAt,
26736
+ ...reasoning[0] ? { sourceTimestamp: reasoning[0].timestamp } : {},
26737
+ status: reasoning.at(-1)?.status ?? "completed",
26738
+ sequence: entry.item.sequence
27197
26739
  }
27198
- }
26740
+ }];
27199
26741
  });
27200
- for (const message of messages) {
27201
- if (message.type !== "subagent_followup") continue;
27202
- const target = messages.find((candidate) => candidate.type === "subagent" && candidate.chatId === message.chatId);
27203
- if (target?.type === "subagent") {
27204
- message.targetTitle = target.description;
27205
- message.targetProvider = target.subagentType;
27206
- }
26742
+ for (const turn of transcript.turns) {
26743
+ const reasoningIndex = coalescedItems.findIndex(({ item, turn: itemTurn }) => itemTurn.id === turn.id && item.type === "reasoning");
26744
+ if (reasoningIndex === -1) continue;
26745
+ const responseItem = getCodexAspTurnResponse({
26746
+ status: turn.status,
26747
+ items: coalescedItems.filter(({ turn: itemTurn }) => itemTurn.id === turn.id).map(({ item }) => item)
26748
+ });
26749
+ const finalAnswerIndex = responseItem ? coalescedItems.findIndex(({ item }) => item === responseItem) : -1;
26750
+ if (finalAnswerIndex === -1 || reasoningIndex < finalAnswerIndex) continue;
26751
+ const [reasoning] = coalescedItems.splice(reasoningIndex, 1);
26752
+ coalescedItems.splice(finalAnswerIndex, 0, reasoning);
27207
26753
  }
27208
- const staleIndexes = new Set(supersededUserMessageIndexes);
27209
- for (const streamId of [...completedStreamIds, ...supersededStreamIds]) {
27210
- const refs = partialIndexes.get(streamId);
27211
- if (refs?.reasoning !== void 0) staleIndexes.add(refs.reasoning);
27212
- if (refs?.agent !== void 0) staleIndexes.add(refs.agent);
26754
+ for (const { item } of coalescedItems) {
26755
+ const message = messageForItem(item);
26756
+ if (message) {
26757
+ messages.push(message);
26758
+ }
26759
+ messages.push(...skillMessagesForItem(item));
27213
26760
  }
27214
- if (staleIndexes.size === 0) return messages;
27215
- return messages.filter((_2, index) => !staleIndexes.has(index));
26761
+ return messages;
27216
26762
  }
27217
26763
 
27218
- // ../shared/src/display-message/parsers/codex-asp-parser.ts
27219
- var DUPLICATE_WINDOW_MS = 5 * 60 * 1e3;
27220
- function nearTimestamp(a, b) {
27221
- return Math.abs(parseTimestampMs(a) - parseTimestampMs(b)) <= DUPLICATE_WINDOW_MS;
27222
- }
27223
- function inputForDisplay(input) {
27224
- if (input === void 0 || input === null) return void 0;
27225
- if (typeof input === "string") return input;
27226
- if (isRecord(input)) return input;
27227
- return String(input);
27228
- }
27229
- function isInternalUserMessage(content) {
27230
- const trimmed = content.trim();
27231
- return trimmed.startsWith("<environment_context>") || trimmed.startsWith("<goal_context>");
27232
- }
27233
- function messageForItem(item) {
27234
- if (item.type === "userMessage") {
27235
- if (isInternalUserMessage(item.content)) return null;
27236
- return {
27237
- id: `user-${item.id}`,
27238
- type: "user",
27239
- content: item.content,
27240
- images: item.images,
27241
- timestamp: item.timestamp
27242
- };
26764
+ // ../shared/src/display-message/parsers/index.ts
26765
+ var INTERRUPTION_DEDUP_WINDOW_MS = 15e3;
26766
+ function parseAgentEvents(events, agentType) {
26767
+ if (agentType === "codex") {
26768
+ return parseCodexEvents(events);
27243
26769
  }
27244
- if (item.type === "agentMessage") {
27245
- if (!item.text) return null;
27246
- return {
27247
- id: `agent-${item.id}`,
27248
- type: "agent",
27249
- content: item.text,
27250
- timestamp: item.timestamp
27251
- };
26770
+ if (agentType === "cursor") {
26771
+ return parseCursorEvents(events);
27252
26772
  }
27253
- if (item.type === "reasoning") {
27254
- if (!item.text) return null;
27255
- return {
27256
- id: `reasoning-${item.id}`,
27257
- type: "reasoning",
27258
- content: item.text,
27259
- status: normalizeCodexAspTranscriptStatus(item.status),
27260
- timestamp: item.timestamp,
27261
- ...item.sourceTimestamp ? { sourceTimestamp: item.sourceTimestamp } : {}
27262
- };
26773
+ if (agentType === "deepseek") {
26774
+ return parseDeepseekEvents(events);
27263
26775
  }
27264
- if (item.type === "commandExecution") {
27265
- const skillNames = skillNamesFromCommand(item.command);
27266
- return {
27267
- id: `command-${item.id}`,
27268
- type: "command",
27269
- command: item.command,
27270
- ...skillNames.length > 0 ? { skillNames } : {},
27271
- output: item.output,
27272
- exitCode: item.exitCode ?? void 0,
27273
- status: normalizeCodexAspTranscriptStatus(item.status),
27274
- timestamp: item.timestamp
27275
- };
26776
+ if (agentType === "fx") {
26777
+ return parseFxEvents(events);
27276
26778
  }
27277
- if (item.type === "fileChange") {
27278
- return {
27279
- id: `patch-${item.id}`,
27280
- type: "patch",
27281
- operations: item.operations,
27282
- output: item.output,
27283
- exitCode: item.exitCode ?? void 0,
27284
- status: normalizeCodexAspTranscriptStatus(item.status),
27285
- timestamp: item.timestamp
27286
- };
26779
+ if (agentType === "kimi") {
26780
+ return parseAcpEvents(events, "kimi");
27287
26781
  }
27288
- if (item.type === "toolCall") {
27289
- return createCallDisplayMessage({
27290
- id: `toolcall-${item.id}`,
27291
- server: item.server,
27292
- tool: item.tool,
27293
- input: inputForDisplay(item.input),
27294
- output: item.output,
27295
- status: normalizeCodexAspTranscriptStatus(item.status),
27296
- timestamp: item.timestamp
27297
- });
26782
+ if (agentType === "muse") {
26783
+ return parseMuseEvents(events);
27298
26784
  }
27299
- if (item.type === "subagent") {
27300
- return {
27301
- id: `subagent-${item.id}`,
27302
- type: "subagent",
27303
- toolUseId: item.id,
27304
- description: item.description,
27305
- prompt: item.prompt,
27306
- subagentType: item.subagentType,
27307
- ...item.receiverThreadIds ? { receiverThreadIds: item.receiverThreadIds } : {},
27308
- ...item.model ? { model: item.model } : {},
27309
- status: normalizeCodexAspTranscriptStatus(item.status),
27310
- ...item.output ? { output: item.output } : {},
27311
- nestedEvents: [],
27312
- timestamp: item.timestamp
27313
- };
26785
+ if (agentType === "opencode") {
26786
+ return parseOpencodeEvents(events);
27314
26787
  }
27315
- if (item.type === "webSearch") {
27316
- return {
27317
- id: `web-search-${item.id}`,
27318
- type: "web_search",
27319
- query: item.query,
27320
- status: normalizeCodexAspTranscriptStatus(item.status),
27321
- timestamp: item.timestamp
27322
- };
26788
+ if (agentType === "pi") {
26789
+ return parsePiEvents(events);
27323
26790
  }
27324
- if (item.type === "plan") {
27325
- if (!item.text) return null;
27326
- return {
27327
- id: `todo-${item.id}`,
27328
- type: "todo_list",
27329
- items: item.text.split("\n").map((line) => line.trim()).filter(Boolean).map((text) => ({ text, completed: item.status === "completed" })),
27330
- status: normalizeCodexAspTranscriptStatus(item.status),
27331
- timestamp: item.timestamp
27332
- };
26791
+ return parseClaudeEvents(events);
26792
+ }
26793
+ function parseDisplayMessages(events, agentType, codexAspTranscript, options = {}) {
26794
+ const shouldFilter = options.filter ?? true;
26795
+ const parsedEvents = agentType === "claude" || agentType === "relay" ? parseClaudeEvents(events, options.parentToolUseId) : parseAgentEvents(events, agentType);
26796
+ const reconciledEvents = [];
26797
+ for (const message of parsedEvents) {
26798
+ if (message.type === "user") upsertDisplayMessage(reconciledEvents, message);
26799
+ else reconciledEvents.push(message);
27333
26800
  }
27334
- if (item.type === "contextCompaction") {
27335
- return {
27336
- id: `reasoning-${item.id}`,
27337
- type: "reasoning",
27338
- content: "Context compacted",
27339
- status: normalizeCodexAspTranscriptStatus(item.status),
27340
- timestamp: item.timestamp
26801
+ const legacyMessages = shouldFilter ? filterDisplayMessages(reconciledEvents, agentType) : reconciledEvents;
26802
+ const applySyntheticNotices = (messages) => shouldFilter ? applyAuthFallbackNotices(applyInterruptions(messages, events), events) : messages;
26803
+ if (agentType !== "codex" || !codexAspTranscript) {
26804
+ return applySyntheticNotices(legacyMessages);
26805
+ }
26806
+ const nativeCodexMessages = shouldFilter ? filterDisplayMessages(parseCodexAspTranscript(codexAspTranscript), agentType) : parseCodexAspTranscript(codexAspTranscript);
26807
+ return applySyntheticNotices(mergeCodexAspDisplayMessages(nativeCodexMessages, legacyMessages));
26808
+ }
26809
+ function applyInterruptions(messages, events) {
26810
+ const result = [...messages];
26811
+ for (const event of events) {
26812
+ if (event.type !== CHAT_INTERRUPTED_EVENT_TYPE) continue;
26813
+ insertDisplayMessageByTimestamp(result, {
26814
+ id: `interruption-${event.timestamp}`,
26815
+ type: "interruption",
26816
+ timestamp: event.timestamp
26817
+ });
26818
+ }
26819
+ let lastUserMs = null;
26820
+ let lastInterruption = null;
26821
+ const finalized = [];
26822
+ for (const msg of result) {
26823
+ if (msg.type === "user") {
26824
+ lastUserMs = parseTimestampMs(msg.timestamp);
26825
+ lastInterruption = null;
26826
+ finalized.push(msg);
26827
+ continue;
26828
+ }
26829
+ if (msg.type !== "interruption") {
26830
+ finalized.push(msg);
26831
+ continue;
26832
+ }
26833
+ const ms = parseTimestampMs(msg.timestamp);
26834
+ if (lastInterruption && ms - parseTimestampMs(lastInterruption.timestamp) <= INTERRUPTION_DEDUP_WINDOW_MS) {
26835
+ continue;
26836
+ }
26837
+ lastInterruption = {
26838
+ ...msg,
26839
+ ...lastUserMs !== null && ms >= lastUserMs ? { durationMs: ms - lastUserMs } : {}
27341
26840
  };
26841
+ finalized.push(lastInterruption);
26842
+ }
26843
+ return finalized;
26844
+ }
26845
+ function applyAuthFallbackNotices(messages, events) {
26846
+ const notices = [];
26847
+ events.forEach((event, index) => {
26848
+ if (event.type !== AUTH_FALLBACK_EVENT_TYPE) return;
26849
+ const payload = coerceAuthFallbackPayload(event.payload);
26850
+ if (!payload) return;
26851
+ notices.push({
26852
+ ...payload,
26853
+ id: `auth-fallback-${event.timestamp}-${index}`,
26854
+ type: "auth_fallback",
26855
+ timestamp: event.timestamp
26856
+ });
26857
+ });
26858
+ if (notices.length === 0) return messages;
26859
+ const result = [...messages];
26860
+ for (const notice of notices) insertDisplayMessageByTimestamp(result, notice);
26861
+ return result;
26862
+ }
26863
+ function isCodexInitializationPrompt(message) {
26864
+ return message.type === "user" && removeReplicasInstructions(message.content).trim() === "Hello";
26865
+ }
26866
+ function filterDisplayMessages(messages, provider) {
26867
+ let result = messages;
26868
+ if (provider === "codex") {
26869
+ const userMessages = result.map((msg, index) => ({ msg, index })).filter(({ msg }) => msg.type === "user");
26870
+ if (userMessages.length >= 2 && isCodexInitializationPrompt(userMessages[0].msg)) {
26871
+ result = result.slice(userMessages[1].index);
26872
+ }
26873
+ }
26874
+ result = result.map((msg) => {
26875
+ if (msg.type !== "user") return msg;
26876
+ const cleaned = removeReplicasInstructions(msg.content).trim();
26877
+ if (INTERRUPTED_MESSAGE_REGEX.test(cleaned)) {
26878
+ return { id: msg.id, type: "interruption", timestamp: msg.timestamp };
26879
+ }
26880
+ return cleaned !== msg.content ? { ...msg, content: cleaned } : msg;
26881
+ });
26882
+ return result;
26883
+ }
26884
+
26885
+ // ../shared/src/agent-event-utils.ts
26886
+ function getUserMessage(event) {
26887
+ return event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string" ? event.payload.message : null;
26888
+ }
26889
+ function getUserMessageId(event) {
26890
+ const messageId = event.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
26891
+ return typeof messageId === "string" ? messageId : null;
26892
+ }
26893
+
26894
+ // ../shared/src/pricing.ts
26895
+ var AUTOMATED_SOURCES = ["api", "automation"];
26896
+ var AUTOMATED_SOURCES_SQL = AUTOMATED_SOURCES.map((s) => `'${s}'`).join(", ");
26897
+ var PLANS = {
26898
+ trial: {
26899
+ id: "trial",
26900
+ name: "Free Trial",
26901
+ monthlyPrice: 0,
26902
+ seatPriceCents: 0,
26903
+ features: [
26904
+ "14 days free, no credit card required",
26905
+ "Unlimited manual workspace minutes",
26906
+ "15,000 automated workspace minutes",
26907
+ "Unlimited repositories and environments",
26908
+ "Unlimited automations",
26909
+ "Warm pools and warm hooks",
26910
+ "API access"
26911
+ ]
26912
+ },
26913
+ developer: {
26914
+ id: "developer",
26915
+ name: "Developer",
26916
+ monthlyPrice: 120,
26917
+ seatPriceCents: 12e3,
26918
+ features: [
26919
+ "Unlimited manual workspace minutes",
26920
+ "5,000 automated workspace minutes per month",
26921
+ "Up to 10 repositories",
26922
+ "Up to 15 environments",
26923
+ "Up to 5 automations",
26924
+ "Warm pools and warm hooks",
26925
+ "API access",
26926
+ "Mobile app"
26927
+ ]
26928
+ },
26929
+ team: {
26930
+ id: "team",
26931
+ name: "Team",
26932
+ monthlyPrice: 300,
26933
+ seatPriceCents: 3e4,
26934
+ features: [
26935
+ "Unlimited manual workspace minutes",
26936
+ "15,000 automated workspace minutes per month",
26937
+ "Unlimited repositories",
26938
+ "Unlimited automations",
26939
+ "Warm pools and warm hooks",
26940
+ "Higher API rate limits",
26941
+ "Access to a static egress IP",
26942
+ "Auto-upgraded sandbox resources (4 vCPU, 32 GB disk, 16 GB memory)",
26943
+ "Shared Slack support channel"
26944
+ ]
26945
+ },
26946
+ enterprise: {
26947
+ id: "enterprise",
26948
+ name: "Enterprise",
26949
+ monthlyPrice: 0,
26950
+ seatPriceCents: 0,
26951
+ features: [
26952
+ "Unlimited manual workspace minutes",
26953
+ "Unlimited automated workspace minutes",
26954
+ "Unlimited automations",
26955
+ "Custom warm hooks and pools",
26956
+ "Custom API rates",
26957
+ "Custom rate limits",
26958
+ "Shared Slack support channel",
26959
+ "SOC 2",
26960
+ "Data Processing Agreement (DPA)",
26961
+ "SCIM user provisioning",
26962
+ "Custom security and privacy settings"
26963
+ ]
27342
26964
  }
27343
- return {
27344
- id: `error-${item.id}`,
27345
- type: "error",
27346
- message: item.message,
27347
- timestamp: item.timestamp
27348
- };
26965
+ };
26966
+ var TEAM_PLAN = PLANS.team;
26967
+ var ENTERPRISE_PLAN = PLANS.enterprise;
26968
+
26969
+ // ../shared/src/port.ts
26970
+ function isValidPort(port) {
26971
+ return Number.isInteger(port) && Number(port) >= 1 && Number(port) <= 65535;
27349
26972
  }
27350
- function skillMessagesForItem(item) {
27351
- if (item.type !== "userMessage" || !item.skillNames) return [];
27352
- return item.skillNames.filter((skillName) => skillName.trim().length > 0).map((skillName, index) => ({
27353
- id: `skill-${item.id}-${index}`,
27354
- type: "skill",
27355
- skillName,
27356
- status: "completed",
27357
- timestamp: item.timestamp
27358
- }));
26973
+ function parsePort(port) {
26974
+ if (!/^\d+$/.test(port)) throw new Error("Port must be a number between 1 and 65535");
26975
+ const portNumber = Number(port);
26976
+ if (!isValidPort(portNumber)) throw new Error("Port must be a number between 1 and 65535");
26977
+ return portNumber;
27359
26978
  }
27360
- function stableString(value) {
27361
- if (value === void 0) return "";
27362
- if (typeof value !== "object" || value === null) return String(value);
27363
- try {
27364
- return JSON.stringify(value, Object.keys(value).sort());
27365
- } catch {
27366
- return String(value);
26979
+
26980
+ // ../shared/src/models-dev.ts
26981
+ var MODELS_DEV_CACHE_MS = 5 * 6e4;
26982
+ var modelsDevCatalogSchema = external_exports.record(external_exports.string(), external_exports.object({
26983
+ models: external_exports.record(external_exports.string(), external_exports.object({
26984
+ id: external_exports.string().optional(),
26985
+ name: external_exports.string(),
26986
+ description: external_exports.string().optional(),
26987
+ status: external_exports.enum(["alpha", "beta", "deprecated"]).optional(),
26988
+ cost: external_exports.object({
26989
+ input: external_exports.number().finite().nonnegative().optional(),
26990
+ output: external_exports.number().finite().nonnegative().optional(),
26991
+ cache_read: external_exports.number().finite().nonnegative().optional(),
26992
+ cache_write: external_exports.number().finite().nonnegative().optional()
26993
+ }).optional()
26994
+ }))
26995
+ }));
26996
+
26997
+ // ../shared/src/sandbox.ts
26998
+ var SANDBOX_LIFECYCLE = {
26999
+ AUTO_STOP_MINUTES: 60,
27000
+ AUTO_ARCHIVE_MINUTES: 60 * 24 * 7,
27001
+ AUTO_DELETE_MINUTES: -1,
27002
+ SSH_TOKEN_EXPIRATION_MINUTES: 3 * 60
27003
+ };
27004
+ var SANDBOX_PATHS = {
27005
+ HOME_DIR: "/home/user",
27006
+ WORKSPACES_DIR: "/home/user/workspaces",
27007
+ REPLICAS_DIR: "/home/user/.replicas",
27008
+ ENGINE_INIT_GATE: "/home/user/.replicas/engine-init-gate",
27009
+ REPLICAS_CANVAS_DIR: "/home/user/.replicas/canvas",
27010
+ REPLICAS_RUNTIME_ENV_FILE: "/home/user/.replicas/runtime-env.sh"
27011
+ };
27012
+ var WORKSPACE_SIZES = ["small", "large"];
27013
+ var INVALID_WORKSPACE_SIZE_ERROR = `Invalid size: must be one of ${WORKSPACE_SIZES.join(", ")}`;
27014
+
27015
+ // ../shared/src/egress.ts
27016
+ var EGRESS_CIPHER = "2022-blake3-aes-256-gcm";
27017
+ var EGRESS_KEY_BYTES = 32;
27018
+ var EGRESS_SHADOWSOCKS_MODE = "tcp_and_udp";
27019
+ var EGRESS_SHADOWSOCKS_VERSION = "1.24.0";
27020
+ var EGRESS_SHADOWSOCKS_SHA256 = "0d84f5f350ec99396867d718f146fc3810975b2a7cd06192f158d96bdef460e7";
27021
+ var EGRESS_PORT_RANGE_START = 8400;
27022
+ var EGRESS_PORT_RANGE_END = 8499;
27023
+ var EGRESS_EXIT_IP_ENDPOINTS = [
27024
+ "https://checkip.amazonaws.com",
27025
+ "https://api.ipify.org",
27026
+ "https://icanhazip.com"
27027
+ ];
27028
+ var EGRESS_DIRECT_ROUTES = [
27029
+ "10.0.0.0/8",
27030
+ "172.16.0.0/12",
27031
+ "192.168.0.0/16",
27032
+ "169.254.0.0/16",
27033
+ "100.64.0.0/10"
27034
+ ];
27035
+ var EGRESS_NEW_CONN_RATE = 100;
27036
+ var EGRESS_NEW_CONN_BURST = 200;
27037
+ var EGRESS_UDP_RATE = 500;
27038
+ var EGRESS_UDP_BURST = 1e3;
27039
+ var EGRESS_OUT_CONN_RATE = 1e3;
27040
+ var EGRESS_OUT_CONN_BURST = 2e3;
27041
+ var EGRESS_DNS_SERVERS = ["1.1.1.1", "8.8.8.8"];
27042
+ var EGRESS_SHELL_CONFIG = [
27043
+ `EGRESS_PORT_RANGE_START=${EGRESS_PORT_RANGE_START}`,
27044
+ `EGRESS_PORT_RANGE_END=${EGRESS_PORT_RANGE_END}`,
27045
+ `EGRESS_SHADOWSOCKS_VERSION=${EGRESS_SHADOWSOCKS_VERSION}`,
27046
+ `EGRESS_SHADOWSOCKS_SHA256=${EGRESS_SHADOWSOCKS_SHA256}`,
27047
+ `EGRESS_CIPHER=${EGRESS_CIPHER}`,
27048
+ `EGRESS_KEY_BYTES=${EGRESS_KEY_BYTES}`,
27049
+ `EGRESS_MODE=${EGRESS_SHADOWSOCKS_MODE}`,
27050
+ `EGRESS_DNS_SERVERS="${EGRESS_DNS_SERVERS.join(" ")}"`,
27051
+ `EGRESS_EXIT_IP_ENDPOINTS="${EGRESS_EXIT_IP_ENDPOINTS.join(" ")}"`,
27052
+ `EGRESS_DIRECT_ROUTES="${EGRESS_DIRECT_ROUTES.join(" ")}"`,
27053
+ `EGRESS_NEW_CONN_RATE=${EGRESS_NEW_CONN_RATE}`,
27054
+ `EGRESS_NEW_CONN_BURST=${EGRESS_NEW_CONN_BURST}`,
27055
+ `EGRESS_UDP_RATE=${EGRESS_UDP_RATE}`,
27056
+ `EGRESS_UDP_BURST=${EGRESS_UDP_BURST}`,
27057
+ `EGRESS_OUT_CONN_RATE=${EGRESS_OUT_CONN_RATE}`,
27058
+ `EGRESS_OUT_CONN_BURST=${EGRESS_OUT_CONN_BURST}`
27059
+ ].join("\n");
27060
+ var EGRESS_TUNNEL_SERVICE = "replicas-egress.service";
27061
+ var EGRESS_TUNNEL_INTERFACE = "rpxtun0";
27062
+ var EGRESS_PATHS = {
27063
+ /**
27064
+ * Under /run, which is tmpfs. The key therefore never touches disk and cannot
27065
+ * be carried into a filesystem snapshot or a paused sandbox's disk image. It
27066
+ * does not survive a reboot either. A full-memory resume can reuse it only
27067
+ * while the root-owned revision still matches the current profile and the
27068
+ * running service and route are healthy.
27069
+ */
27070
+ CONFIG_DIR: "/run/replicas-egress",
27071
+ CONFIG_FILE: "/run/replicas-egress/config.json",
27072
+ REVISION_FILE: "/run/replicas-egress/revision",
27073
+ /** Persistent: this records the workspace's original resolver so the tunnel can put it back, and must outlive a reboot. */
27074
+ RESOLV_BACKUP: "/var/lib/replicas-egress-resolv.conf.orig",
27075
+ UNIT_FILE: `/etc/systemd/system/${EGRESS_TUNNEL_SERVICE}`,
27076
+ UP_SCRIPT: "/usr/local/lib/replicas-egress-up",
27077
+ DOWN_SCRIPT: "/usr/local/lib/replicas-egress-down"
27078
+ };
27079
+ var EGRESS_WORKSPACE_CONFIG_FILE = "/opt/replicas/egress-config.env";
27080
+ var EGRESS_WORKSPACE_SHELL_VARS = {
27081
+ EGRESS_WORKSPACE_CONFIG_FILE,
27082
+ EGRESS_TUNNEL_SERVICE,
27083
+ EGRESS_TUNNEL_INTERFACE,
27084
+ EGRESS_DNS_SERVERS: EGRESS_DNS_SERVERS.join(" "),
27085
+ EGRESS_EXIT_IP_ENDPOINTS: EGRESS_EXIT_IP_ENDPOINTS.join(" ")
27086
+ };
27087
+
27088
+ // ../shared/src/replicas-config.ts
27089
+ var import_yaml = __toESM(require_dist());
27090
+
27091
+ // ../shared/src/warm-hooks.ts
27092
+ var DEFAULT_WARM_HOOK_TIMEOUT_MS = 60 * 60 * 1e3;
27093
+ var MAX_WARM_HOOK_TIMEOUT_MS = 120 * 60 * 1e3;
27094
+ var DEFAULT_START_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
27095
+ var HOOK_EXEC_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
27096
+
27097
+ // ../shared/src/replicas-config.ts
27098
+ var REPLICAS_CONFIG_FILENAMES = ["replicas.json", "replicas.yaml", "replicas.yml"];
27099
+
27100
+ // ../shared/src/headless-agent.ts
27101
+ var headlessAgentRequestBaseSchema = external_exports.object({
27102
+ agent: external_exports.custom(
27103
+ (value) => typeof value === "string" && isValidCodingAgentProvider(value)
27104
+ ),
27105
+ model: external_exports.string().min(1),
27106
+ prompt: external_exports.string(),
27107
+ workingDirectory: external_exports.string().min(1),
27108
+ timeoutSeconds: external_exports.number().int().min(1).max(1800).default(300),
27109
+ codexOauthTokens: external_exports.object({ accessToken: external_exports.string(), accountId: external_exports.string() }).optional()
27110
+ });
27111
+ var headlessAgentInputFileSchema = external_exports.object({
27112
+ path: external_exports.string().min(1),
27113
+ downloadUrl: external_exports.url()
27114
+ });
27115
+ var headlessAgentOutputFileSchema = external_exports.object({
27116
+ path: external_exports.string().min(1),
27117
+ uploadUrl: external_exports.url(),
27118
+ contentType: external_exports.string().min(1),
27119
+ minChars: external_exports.number().int().positive().optional(),
27120
+ maxChars: external_exports.number().int().positive().optional()
27121
+ });
27122
+ var headlessAgentRequestSchema = external_exports.discriminatedUnion("mode", [
27123
+ headlessAgentRequestBaseSchema.extend({
27124
+ mode: external_exports.literal("structured"),
27125
+ outputSchema: external_exports.record(external_exports.string(), external_exports.json())
27126
+ }),
27127
+ headlessAgentRequestBaseSchema.extend({
27128
+ mode: external_exports.literal("filesystem"),
27129
+ inputFiles: external_exports.array(headlessAgentInputFileSchema),
27130
+ outputFiles: external_exports.array(headlessAgentOutputFileSchema),
27131
+ sensitiveValues: external_exports.array(external_exports.string())
27132
+ })
27133
+ ]);
27134
+ var headlessFilesystemAgentResultSchema = external_exports.object({
27135
+ message: external_exports.string(),
27136
+ files: external_exports.record(external_exports.string(), external_exports.object({
27137
+ sha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
27138
+ sizeBytes: external_exports.number().int().nonnegative()
27139
+ }))
27140
+ });
27141
+
27142
+ // ../shared/src/cli-version.ts
27143
+ var CLI_VERSION = "0.2.604";
27144
+
27145
+ // ../shared/src/version.ts
27146
+ function compareVersions(v1, v2) {
27147
+ const parts1 = v1.split(".").map(Number);
27148
+ const parts2 = v2.split(".").map(Number);
27149
+ for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
27150
+ const num1 = parts1[i] || 0;
27151
+ const num2 = parts2[i] || 0;
27152
+ if (num1 > num2) return 1;
27153
+ if (num1 < num2) return -1;
27367
27154
  }
27155
+ return 0;
27368
27156
  }
27369
- function duplicateKey(message) {
27370
- if (message.type === "user" || message.type === "agent" || message.type === "reasoning") {
27371
- return JSON.stringify([message.type, message.content]);
27372
- }
27373
- if (message.type === "command") return JSON.stringify([message.type, message.command]);
27374
- if (message.type === "web_search") return JSON.stringify([message.type, message.query]);
27375
- if (message.type === "subagent") {
27376
- return JSON.stringify([
27377
- message.type,
27378
- message.description,
27379
- message.prompt,
27380
- message.subagentType,
27381
- message.model
27382
- ]);
27383
- }
27384
- if (message.type === "todo_list") {
27385
- return JSON.stringify([message.type, stableString(message.items)]);
27386
- }
27387
- if (message.type === "patch") {
27388
- return JSON.stringify([message.type, stableString(message.operations)]);
27389
- }
27390
- if (message.type === "tool_call") {
27391
- return JSON.stringify([
27392
- message.type,
27393
- message.server,
27394
- message.tool,
27395
- stableString(message.input)
27396
- ]);
27157
+
27158
+ // ../shared/src/routes/plugin-catalog.ts
27159
+ var EXPLICIT_PLUGIN_CATALOG = [
27160
+ {
27161
+ id: "attio",
27162
+ backend: "composio",
27163
+ toolkit: "attio",
27164
+ authType: "oauth",
27165
+ category: "business",
27166
+ name: "Attio",
27167
+ description: "Manage CRM records, people, companies, lists, and notes."
27168
+ },
27169
+ {
27170
+ id: "calendly",
27171
+ backend: "composio",
27172
+ toolkit: "calendly",
27173
+ authType: "oauth",
27174
+ category: "productivity",
27175
+ name: "Calendly",
27176
+ description: "Manage event types, scheduled events, invitees, and scheduling links."
27177
+ },
27178
+ {
27179
+ id: "clickhouse",
27180
+ backend: "composio",
27181
+ toolkit: "clickhouse",
27182
+ authType: "basic",
27183
+ category: "data",
27184
+ name: "ClickHouse",
27185
+ description: "Manage databases, tables, schemas, and queries."
27186
+ },
27187
+ {
27188
+ id: "cloudflare",
27189
+ backend: "composio",
27190
+ toolkit: "cloudflare_api_key",
27191
+ authType: "api_key",
27192
+ category: "data",
27193
+ name: "Cloudflare",
27194
+ description: "Manage zones, DNS records, analytics, and security settings."
27195
+ },
27196
+ {
27197
+ id: "e2b",
27198
+ toolkit: "e2b",
27199
+ backend: "native",
27200
+ authType: "api_key",
27201
+ category: "data",
27202
+ name: "E2B",
27203
+ description: "Create secure cloud sandboxes and run commands in them."
27204
+ },
27205
+ {
27206
+ id: "gmail",
27207
+ backend: "composio",
27208
+ toolkit: "gmail",
27209
+ authType: "oauth",
27210
+ category: "productivity",
27211
+ name: "Gmail",
27212
+ description: "Manage messages, threads, attachments, labels, and mailbox settings."
27213
+ },
27214
+ {
27215
+ id: "googleads",
27216
+ backend: "composio",
27217
+ toolkit: "googleads",
27218
+ authType: "oauth",
27219
+ category: "data",
27220
+ name: "Google Ads",
27221
+ description: "Manage campaigns, customers, accounts, and reporting data."
27222
+ },
27223
+ {
27224
+ id: "googledocs",
27225
+ backend: "composio",
27226
+ toolkit: "googledocs",
27227
+ authType: "oauth",
27228
+ category: "productivity",
27229
+ name: "Google Docs",
27230
+ description: "Manage documents, structure, comments, and exported content."
27231
+ },
27232
+ {
27233
+ id: "googledrive",
27234
+ backend: "composio",
27235
+ toolkit: "googledrive",
27236
+ authType: "oauth",
27237
+ category: "productivity",
27238
+ name: "Google Drive",
27239
+ description: "Manage files, folders, metadata, permissions, and shared drives."
27240
+ },
27241
+ {
27242
+ id: "googlesearch",
27243
+ backend: "composio",
27244
+ toolkit: "google_search_console",
27245
+ authType: "oauth",
27246
+ category: "data",
27247
+ name: "Google Search",
27248
+ description: "Manage Search Console performance, indexing, sites, and sitemaps."
27249
+ },
27250
+ {
27251
+ id: "googlesheets",
27252
+ backend: "composio",
27253
+ toolkit: "googlesheets",
27254
+ authType: "oauth",
27255
+ category: "productivity",
27256
+ name: "Google Sheets",
27257
+ description: "Manage spreadsheets, worksheets, ranges, tables, and charts."
27258
+ },
27259
+ {
27260
+ id: "linkedin",
27261
+ backend: "composio",
27262
+ toolkit: "linkedin",
27263
+ authType: "oauth",
27264
+ category: "business",
27265
+ name: "LinkedIn",
27266
+ description: "Manage profiles, organizations, posts, and professional activity."
27267
+ },
27268
+ {
27269
+ id: "modal",
27270
+ backend: "native",
27271
+ toolkit: "modal",
27272
+ authType: "basic",
27273
+ category: "data",
27274
+ name: "Modal",
27275
+ description: "Invoke deployed functions through Modal compute."
27276
+ },
27277
+ {
27278
+ id: "posthog",
27279
+ backend: "composio",
27280
+ toolkit: "posthog",
27281
+ authType: "api_key",
27282
+ category: "data",
27283
+ name: "PostHog",
27284
+ description: "Manage projects, events, insights, dashboards, flags, and recordings."
27285
+ },
27286
+ {
27287
+ id: "planetscale",
27288
+ toolkit: "planetscale",
27289
+ backend: "native",
27290
+ authType: "oauth",
27291
+ category: "data",
27292
+ name: "PlanetScale",
27293
+ description: "Manage organizations, databases, branches, deploy requests, and Insights."
27294
+ },
27295
+ {
27296
+ id: "stripe",
27297
+ backend: "composio",
27298
+ toolkit: "stripe",
27299
+ authType: "oauth",
27300
+ category: "business",
27301
+ name: "Stripe",
27302
+ description: "Manage customers, payments, invoices, subscriptions, and balances."
27303
+ },
27304
+ {
27305
+ id: "pangram",
27306
+ backend: "native",
27307
+ toolkit: "pangram",
27308
+ authType: "api_key",
27309
+ category: "data",
27310
+ name: "Pangram",
27311
+ description: "Analyze text for AI generation and plagiarism signals."
27312
+ },
27313
+ {
27314
+ id: "vercel",
27315
+ backend: "native",
27316
+ toolkit: "vercel",
27317
+ authType: "api_key",
27318
+ category: "data",
27319
+ name: "Vercel",
27320
+ description: "Manage projects, deployments, logs, domains, and infrastructure."
27321
+ },
27322
+ {
27323
+ id: "turbopuffer",
27324
+ backend: "native",
27325
+ toolkit: "turbopuffer",
27326
+ authType: "api_key",
27327
+ category: "data",
27328
+ name: "turbopuffer",
27329
+ description: "Query and manage turbopuffer namespaces."
27330
+ },
27331
+ {
27332
+ id: "vanta",
27333
+ backend: "native",
27334
+ toolkit: "vanta",
27335
+ authType: "credentials",
27336
+ category: "data",
27337
+ name: "Vanta",
27338
+ description: "Review and manage controls, tests, evidence, vendors, risks, and compliance data.",
27339
+ scopes: ["environment"]
27397
27340
  }
27398
- return null;
27399
- }
27400
- function duplicateBucketKey(message, bucketOffset = 0) {
27401
- const key = duplicateKey(message);
27402
- if (key === null) return null;
27403
- const bucket = Math.floor(parseTimestampMs(message.timestamp) / DUPLICATE_WINDOW_MS) + bucketOffset;
27404
- return JSON.stringify([bucket, key]);
27341
+ ];
27342
+ var HOSTED_COMPOSIO_PLUGIN_DEFINITIONS = [
27343
+ ["notion", "notion", "OAUTH2", true, "productivity", "Notion"],
27344
+ ["jira", "jira", "OAUTH2", true, "productivity", "Jira"],
27345
+ ["confluence", "confluence", "OAUTH2", true, "productivity", "Confluence"],
27346
+ ["googlecalendar", "googlecalendar", "OAUTH2", true, "productivity", "Google Calendar"],
27347
+ ["microsoftteams", "microsoft_teams", "OAUTH2", true, "productivity", "Microsoft Teams"],
27348
+ ["outlook", "outlook", "OAUTH2", true, "productivity", "Outlook"],
27349
+ ["bitbucket", "bitbucket", "OAUTH2", true, "data", "Bitbucket"],
27350
+ ["datadog", "datadog", "API_KEY", false, "data", "Datadog"],
27351
+ ["pagerduty", "pagerduty", "OAUTH2", true, "data", "PagerDuty"],
27352
+ ["intercom", "intercom", "OAUTH2", true, "business", "Intercom"],
27353
+ ["zendesk", "zendesk", "OAUTH2", true, "business", "Zendesk"],
27354
+ ["hubspot", "hubspot", "OAUTH2", true, "business", "HubSpot"],
27355
+ ["supabase", "supabase", "OAUTH2", true, "data", "Supabase"],
27356
+ ["figma", "figma", "OAUTH2", true, "productivity", "Figma"],
27357
+ ["launchdarkly", "launch_darkly", "API_KEY", false, "data", "LaunchDarkly"],
27358
+ ["asana", "asana", "OAUTH2", true, "productivity", "Asana"],
27359
+ ["clickup", "clickup", "OAUTH2", true, "productivity", "ClickUp"],
27360
+ ["trello", "trello", "OAUTH1", true, "productivity", "Trello"],
27361
+ ["todoist", "todoist", "OAUTH2", true, "productivity", "Todoist"],
27362
+ ["airtable", "airtable", "OAUTH2", true, "productivity", "Airtable"],
27363
+ ["coda", "coda", "API_KEY", false, "productivity", "Coda"],
27364
+ ["miro", "miro", "OAUTH2", true, "productivity", "Miro"],
27365
+ ["sharepoint", "share_point", "OAUTH2", true, "productivity", "SharePoint"],
27366
+ ["onedrive", "one_drive", "OAUTH2", true, "productivity", "OneDrive"],
27367
+ ["googleslides", "googleslides", "OAUTH2", true, "productivity", "Google Slides"],
27368
+ ["googlemeet", "googlemeet", "OAUTH2", true, "productivity", "Google Meet"],
27369
+ ["googletasks", "googletasks", "OAUTH2", true, "productivity", "Google Tasks"],
27370
+ ["dropbox", "dropbox", "OAUTH2", true, "productivity", "Dropbox"],
27371
+ ["box", "box", "OAUTH2", true, "productivity", "Box"],
27372
+ ["discord", "discord", "OAUTH2", true, "productivity", "Discord"],
27373
+ ["discordbot", "discordbot", "OAUTH2", true, "productivity", "Discord Bot"],
27374
+ ["zoom", "zoom", "OAUTH2", true, "productivity", "Zoom"],
27375
+ ["googlechat", "google_chat", "OAUTH2", false, "productivity", "Google Chat"],
27376
+ ["newrelic", "new_relic", "API_KEY", false, "data", "New Relic"],
27377
+ ["betterstack", "better_stack", "API_KEY", false, "data", "Better Stack"],
27378
+ ["incidentio", "incident_io", "API_KEY", false, "data", "incident.io"],
27379
+ ["grafana", "grafana", "BEARER_TOKEN", false, "data", "Grafana"],
27380
+ ["honeycomb", "honeycomb_mcp", "DCR_OAUTH", false, "data", "Honeycomb MCP"],
27381
+ ["bugsnag", "bugsnag", "API_KEY", false, "data", "Bugsnag"],
27382
+ ["circleci", "circleci", "API_KEY", false, "data", "CircleCI"],
27383
+ ["buildkite", "buildkite", "API_KEY", false, "data", "Buildkite"],
27384
+ ["dockerhub", "docker_hub", "API_KEY", false, "data", "Docker Hub"],
27385
+ ["digitalocean", "digital_ocean", "OAUTH2", true, "data", "DigitalOcean"],
27386
+ ["railway", "railway", "API_KEY", false, "data", "Railway"],
27387
+ ["render", "render", "API_KEY", false, "data", "Render"],
27388
+ ["firebase", "firebase", "API_KEY", false, "data", "Firebase"],
27389
+ ["cloudinary", "cloudinary", "API_KEY", false, "data", "Cloudinary"],
27390
+ ["configcat", "configcat", "API_KEY", false, "data", "ConfigCat"],
27391
+ ["contextdev", "context_dev", "API_KEY", false, "data", "Context.dev"],
27392
+ ["googleanalytics", "google_analytics", "OAUTH2", true, "data", "Google Analytics"],
27393
+ ["googlebigquery", "googlebigquery", "OAUTH2", true, "data", "Google BigQuery"],
27394
+ ["amplitude", "amplitude", "API_KEY", false, "data", "Amplitude"],
27395
+ ["mixpanel", "mixpanel", "BASIC", false, "data", "Mixpanel"],
27396
+ ["segment", "segment", "API_KEY", false, "data", "Segment"],
27397
+ ["databricks", "databricks", "API_KEY", false, "data", "Databricks"],
27398
+ ["snowflake", "snowflake", "OAUTH2", false, "data", "Snowflake"],
27399
+ ["algolia", "algolia", "API_KEY", false, "data", "Algolia"],
27400
+ ["elasticsearch", "elasticsearch", "API_KEY", false, "data", "Elasticsearch"],
27401
+ ["salesforce", "salesforce", "OAUTH2", true, "business", "Salesforce"],
27402
+ ["pipedrive", "pipedrive", "API_KEY", false, "business", "Pipedrive"],
27403
+ ["close", "close", "API_KEY", false, "business", "Close"],
27404
+ ["apollo", "apollo", "API_KEY", false, "business", "Apollo"],
27405
+ ["gong", "gong", "OAUTH2", true, "business", "Gong"],
27406
+ ["freshdesk", "freshdesk", "API_KEY", false, "business", "Freshdesk"],
27407
+ ["helpscout", "help_scout", "OAUTH2", false, "business", "Help Scout"],
27408
+ ["servicenow", "servicenow", "BASIC", false, "business", "ServiceNow"],
27409
+ ["mailchimp", "mailchimp", "OAUTH2", true, "business", "Mailchimp"],
27410
+ ["customerio", "customerio", "API_KEY", false, "business", "Customer.io"],
27411
+ ["klaviyo", "klaviyo", "API_KEY", false, "business", "Klaviyo"],
27412
+ ["shopify", "shopify", "API_KEY", false, "business", "Shopify"],
27413
+ ["quickbooks", "quickbooks", "OAUTH2", true, "business", "QuickBooks"],
27414
+ ["xero", "xero", "OAUTH2", false, "business", "Xero"],
27415
+ ["brex", "brex", "API_KEY", false, "business", "Brex"],
27416
+ ["buffer", "buffer", "OAUTH2", false, "business", "Buffer"],
27417
+ ["canva", "canva", "OAUTH2", true, "productivity", "Canva"],
27418
+ ["webflow", "webflow", "API_KEY", false, "business", "Webflow"],
27419
+ ["firecrawl", "firecrawl", "API_KEY", false, "data", "Firecrawl"],
27420
+ ["browserbase", "browserbase_tool", "API_KEY", false, "data", "Browserbase"],
27421
+ ["exa", "exa", "API_KEY", false, "data", "Exa"],
27422
+ ["youtube", "youtube", "OAUTH2", true, "business", "YouTube"],
27423
+ ["twitter", "twitter", "OAUTH2", false, "business", "Twitter/X"],
27424
+ ["instagram", "instagram", "OAUTH2", true, "business", "Instagram"],
27425
+ ["facebook", "facebook", "OAUTH2", true, "business", "Facebook"]
27426
+ ];
27427
+ var HOSTED_COMPOSIO_PLUGINS = HOSTED_COMPOSIO_PLUGIN_DEFINITIONS.map(
27428
+ ([id, toolkit, authScheme, managedAuth, category, name]) => ({
27429
+ id,
27430
+ backend: "composio",
27431
+ toolkit,
27432
+ authType: "hosted",
27433
+ authScheme,
27434
+ managedAuth,
27435
+ category,
27436
+ name,
27437
+ description: `Use ${name} tools from workspace agents.`
27438
+ })
27439
+ );
27440
+ var PLUGIN_CATALOG = [
27441
+ ...EXPLICIT_PLUGIN_CATALOG,
27442
+ ...HOSTED_COMPOSIO_PLUGINS
27443
+ ];
27444
+
27445
+ // ../shared/src/routes/user.ts
27446
+ var PROFILE_AVATAR_MAX_SIZE_BYTES = 5 * 1024 * 1024;
27447
+
27448
+ // ../shared/src/routes/client-state.ts
27449
+ var MAX_CLIENT_STATE_REQUEST_BYTES = 5 * 1024 * 1024;
27450
+ var MAX_CLIENT_STATE_BYTES_PER_USER = 10 * 1024 * 1024;
27451
+
27452
+ // ../shared/src/routes/workspaces.ts
27453
+ var WORKSPACE_STATUSES = ["active", "sleeping", "archived", "preparing", "error"];
27454
+ function isWorkspaceSuspendedStatus(status) {
27455
+ return status === "sleeping" || status === "archived";
27405
27456
  }
27406
- function mergeCodexAspDisplayMessages(primary, supplemental) {
27407
- const merged = [...primary];
27408
- const primaryCount = primary.length;
27409
- const firstIndexById = /* @__PURE__ */ new Map();
27410
- const indexesByDuplicateBucket = /* @__PURE__ */ new Map();
27411
- const indexMessage = (message, index) => {
27412
- if (!firstIndexById.has(message.id)) firstIndexById.set(message.id, index);
27413
- const key = duplicateBucketKey(message);
27414
- if (key === null) return;
27415
- const indexes = indexesByDuplicateBucket.get(key) ?? /* @__PURE__ */ new Set();
27416
- indexes.add(index);
27417
- indexesByDuplicateBucket.set(key, indexes);
27418
- };
27419
- const unindexMessage = (message, index) => {
27420
- const key = duplicateBucketKey(message);
27421
- if (key === null) return;
27422
- const indexes = indexesByDuplicateBucket.get(key);
27423
- indexes?.delete(index);
27424
- if (indexes?.size === 0) indexesByDuplicateBucket.delete(key);
27457
+ function workspaceConfigWithCapabilities(config3, capabilities = {}) {
27458
+ const mergedCapabilities = {
27459
+ ...config3?.capabilities,
27460
+ ...capabilities
27425
27461
  };
27426
- merged.forEach(indexMessage);
27427
- for (const message of supplemental) {
27428
- let duplicateIndex = firstIndexById.get(message.id);
27429
- if (duplicateIndex === void 0) {
27430
- for (let bucketOffset = -1; bucketOffset <= 1; bucketOffset += 1) {
27431
- const key = duplicateBucketKey(message, bucketOffset);
27432
- for (const index of (key === null ? void 0 : indexesByDuplicateBucket.get(key)) ?? []) {
27433
- if (nearTimestamp(merged[index].timestamp, message.timestamp) && (duplicateIndex === void 0 || index < duplicateIndex)) {
27434
- duplicateIndex = index;
27435
- }
27436
- }
27437
- }
27438
- }
27439
- if (duplicateIndex === void 0) {
27440
- indexMessage(message, merged.push(message) - 1);
27441
- } else if (message.type === "user") {
27442
- unindexMessage(merged[duplicateIndex], duplicateIndex);
27443
- merged[duplicateIndex] = {
27444
- ...merged[duplicateIndex],
27445
- id: message.id
27446
- };
27447
- firstIndexById.set(message.id, duplicateIndex);
27448
- indexMessage(merged[duplicateIndex], duplicateIndex);
27462
+ return {
27463
+ ...config3,
27464
+ capabilities: {
27465
+ ...mergedCapabilities,
27466
+ pr_followups: mergedCapabilities.pr_followups === true
27449
27467
  }
27450
- }
27451
- const ordered = merged.slice(0, primaryCount);
27452
- const supplementalOnly = merged.slice(primaryCount).map((message, index) => ({ message, index })).sort((a, b) => parseTimestampMs(a.message.timestamp) - parseTimestampMs(b.message.timestamp) || a.index - b.index);
27453
- for (const { message } of supplementalOnly) {
27454
- insertDisplayMessageByTimestamp(ordered, message);
27455
- }
27456
- return ordered;
27468
+ };
27457
27469
  }
27458
- function parseCodexAspTranscript(transcript) {
27459
- const messages = [];
27460
- const orderedItems = transcript.turns.map((turn, turnIndex) => ({ turn, turnIndex })).flatMap(({ turn, turnIndex }) => turn.items.map((item, itemIndex) => ({ item, turn, turnIndex, itemIndex }))).sort((a, b) => {
27461
- const aSequence = a.item.sequence ?? Number.MAX_SAFE_INTEGER;
27462
- const bSequence = b.item.sequence ?? Number.MAX_SAFE_INTEGER;
27463
- return aSequence - bSequence || parseTimestampMs(a.item.timestamp) - parseTimestampMs(b.item.timestamp) || a.turnIndex - b.turnIndex || a.itemIndex - b.itemIndex;
27464
- });
27465
- const reasoningByTurnId = /* @__PURE__ */ new Map();
27466
- for (const { item, turn } of orderedItems) {
27467
- if (item.type !== "reasoning" || !item.text.trim()) continue;
27468
- const reasoning = reasoningByTurnId.get(turn.id) ?? [];
27469
- reasoning.push(item);
27470
- reasoningByTurnId.set(turn.id, reasoning);
27471
- }
27472
- const emittedReasoningTurnIds = /* @__PURE__ */ new Set();
27473
- const coalescedItems = orderedItems.flatMap((entry) => {
27474
- if (entry.item.type !== "reasoning") return [entry];
27475
- const reasoning = reasoningByTurnId.get(entry.turn.id);
27476
- if (!reasoning || emittedReasoningTurnIds.has(entry.turn.id)) return [];
27477
- emittedReasoningTurnIds.add(entry.turn.id);
27478
- return [{
27479
- ...entry,
27480
- item: {
27481
- type: "reasoning",
27482
- id: entry.turn.id,
27483
- text: reasoning.map(({ text }) => text.trim()).join("\n\n"),
27484
- timestamp: entry.turn.startedAt,
27485
- ...reasoning[0] ? { sourceTimestamp: reasoning[0].timestamp } : {},
27486
- status: reasoning.at(-1)?.status ?? "completed",
27487
- sequence: entry.item.sequence
27488
- }
27489
- }];
27490
- });
27491
- for (const turn of transcript.turns) {
27492
- const reasoningIndex = coalescedItems.findIndex(({ item, turn: itemTurn }) => itemTurn.id === turn.id && item.type === "reasoning");
27493
- if (reasoningIndex === -1) continue;
27494
- const responseItem = getCodexAspTurnResponse({
27495
- status: turn.status,
27496
- items: coalescedItems.filter(({ turn: itemTurn }) => itemTurn.id === turn.id).map(({ item }) => item)
27497
- });
27498
- const finalAnswerIndex = responseItem ? coalescedItems.findIndex(({ item }) => item === responseItem) : -1;
27499
- if (finalAnswerIndex === -1 || reasoningIndex < finalAnswerIndex) continue;
27500
- const [reasoning] = coalescedItems.splice(reasoningIndex, 1);
27501
- coalescedItems.splice(finalAnswerIndex, 0, reasoning);
27502
- }
27503
- for (const { item } of coalescedItems) {
27504
- const message = messageForItem(item);
27505
- if (message) {
27506
- messages.push(message);
27507
- }
27508
- messages.push(...skillMessagesForItem(item));
27509
- }
27510
- return messages;
27470
+ function workspaceConfigWithPrFollowups(config3, prFollowups = config3?.capabilities?.pr_followups === true) {
27471
+ return workspaceConfigWithCapabilities(config3, { pr_followups: prFollowups });
27472
+ }
27473
+ var WORKSPACE_STATUS_FILTERS = WORKSPACE_STATUSES.map((status) => status === "error" ? "failed" : status);
27474
+ var WORKSPACE_SORT_CHOICES = [
27475
+ ["manual", "Custom"],
27476
+ ["activity", "Last activity"],
27477
+ ["created", "Created date"]
27478
+ ];
27479
+ var WORKSPACE_SORT_OPTIONS = WORKSPACE_SORT_CHOICES.map(([option]) => option);
27480
+ var WORKSPACE_SORT_LABELS = Object.fromEntries(WORKSPACE_SORT_CHOICES);
27481
+ var WORKSPACE_SIDEBAR_ORGANIZATION_CHOICES = [
27482
+ ["chronological", "Chronological"],
27483
+ ["environments", "Environments"],
27484
+ ["recent", "Recent environments"]
27485
+ ];
27486
+ var WORKSPACE_SIDEBAR_ORGANIZATIONS = WORKSPACE_SIDEBAR_ORGANIZATION_CHOICES.map(([organization]) => organization);
27487
+ var WORKSPACE_SIDEBAR_ORGANIZATION_LABELS = Object.fromEntries(WORKSPACE_SIDEBAR_ORGANIZATION_CHOICES);
27488
+ var DEFAULT_STATUS_FILTERS = [...WORKSPACE_STATUS_FILTERS];
27489
+ var DEFAULT_WORKSPACE_FILTERS = {
27490
+ statuses: [...DEFAULT_STATUS_FILTERS],
27491
+ ownerIds: [],
27492
+ activity: "any"
27493
+ };
27494
+ var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
27495
+
27496
+ // ../shared/src/routes/workspace-events.ts
27497
+ var workspaceChangedEventSchema = external_exports.discriminatedUnion("type", [
27498
+ external_exports.object({
27499
+ type: external_exports.literal("workspace.changed"),
27500
+ workspaceId: external_exports.string(),
27501
+ completion: external_exports.object({
27502
+ workspaceName: external_exports.string(),
27503
+ completedAt: external_exports.string(),
27504
+ processing: external_exports.boolean().optional()
27505
+ }).optional(),
27506
+ ts: external_exports.string()
27507
+ }),
27508
+ external_exports.object({
27509
+ type: external_exports.literal("workspace.chat.changed"),
27510
+ workspaceId: external_exports.string(),
27511
+ chatId: external_exports.string(),
27512
+ ts: external_exports.string()
27513
+ }),
27514
+ external_exports.object({ type: external_exports.literal("presence.changed"), ts: external_exports.string() }),
27515
+ external_exports.object({
27516
+ type: external_exports.literal("mobile-testing.changed"),
27517
+ workspaceId: external_exports.string(),
27518
+ ts: external_exports.string()
27519
+ })
27520
+ ]);
27521
+
27522
+ // ../shared/src/routes/presence.ts
27523
+ var presenceStatusSchema = external_exports.enum(["online", "typing"]);
27524
+ var presenceLocationSchema = external_exports.object({
27525
+ environmentId: external_exports.string().optional(),
27526
+ workspaceId: external_exports.string().optional()
27527
+ });
27528
+ var presenceEntrySchema = external_exports.object({
27529
+ userId: external_exports.string(),
27530
+ status: presenceStatusSchema,
27531
+ location: presenceLocationSchema,
27532
+ ts: external_exports.string()
27533
+ });
27534
+ var updatePresenceRequestSchema = external_exports.object({
27535
+ status: presenceStatusSchema,
27536
+ location: presenceLocationSchema.optional()
27537
+ });
27538
+
27539
+ // ../shared/src/audit-log.ts
27540
+ var AUDIT_LOG_ACTION = {
27541
+ CREATE: "create",
27542
+ UPDATE: "update",
27543
+ DELETE: "delete",
27544
+ CONNECT: "connect",
27545
+ DISCONNECT: "disconnect"
27546
+ };
27547
+ var AUDIT_LOG_ACTIONS = Object.values(AUDIT_LOG_ACTION);
27548
+
27549
+ // ../shared/src/automations/types.ts
27550
+ var AUTOMATION_DEBOUNCE_MAX_SECONDS = 24 * 60 * 60;
27551
+ var AUTOMATION_LIFECYCLE_POLICIES = ["default", "archive_when_done", "sleep_when_done"];
27552
+ function toAutomationLifecyclePolicy(policy) {
27553
+ if (!policy) return "default";
27554
+ if (policy === "default" || policy === "archive_when_done" || policy === "sleep_when_done") return policy;
27555
+ return "archive_when_done";
27556
+ }
27557
+ function lifecyclePolicySupportsAutoStop(policy) {
27558
+ return policy === "default";
27511
27559
  }
27512
27560
 
27513
- // ../shared/src/display-message/parsers/index.ts
27514
- var INTERRUPTION_DEDUP_WINDOW_MS = 15e3;
27515
- function parseAgentEvents(events, agentType) {
27516
- if (agentType === "codex") {
27517
- return parseCodexEvents(events);
27518
- }
27519
- if (agentType === "cursor") {
27520
- return parseCursorEvents(events);
27521
- }
27522
- if (agentType === "deepseek") {
27523
- return parseDeepseekEvents(events);
27524
- }
27525
- if (agentType === "fx") {
27526
- return parseFxEvents(events);
27527
- }
27528
- if (agentType === "kimi") {
27529
- return parseAcpEvents(events, "kimi");
27530
- }
27531
- if (agentType === "muse") {
27532
- return parseMuseEvents(events);
27533
- }
27534
- if (agentType === "opencode") {
27535
- return parseOpencodeEvents(events);
27536
- }
27537
- if (agentType === "pi") {
27538
- return parsePiEvents(events);
27539
- }
27540
- return parseClaudeEvents(events);
27561
+ // ../shared/src/automations/github-checks.ts
27562
+ var AUTOMATION_CHECK_CONCLUSIONS = ["success", "failure"];
27563
+ function isAutomationCheckConclusion(value) {
27564
+ return AUTOMATION_CHECK_CONCLUSIONS.some((conclusion) => conclusion === value);
27541
27565
  }
27542
- function parseDisplayMessages(events, agentType, codexAspTranscript, options = {}) {
27543
- const shouldFilter = options.filter ?? true;
27544
- const parsedEvents = agentType === "claude" || agentType === "relay" ? parseClaudeEvents(events, options.parentToolUseId) : parseAgentEvents(events, agentType);
27545
- const reconciledEvents = [];
27546
- for (const message of parsedEvents) {
27547
- if (message.type === "user") upsertDisplayMessage(reconciledEvents, message);
27548
- else reconciledEvents.push(message);
27549
- }
27550
- const legacyMessages = shouldFilter ? filterDisplayMessages(reconciledEvents, agentType) : reconciledEvents;
27551
- const applySyntheticNotices = (messages) => shouldFilter ? applyAuthFallbackNotices(applyInterruptions(messages, events), events) : messages;
27552
- if (agentType !== "codex" || !codexAspTranscript) {
27553
- return applySyntheticNotices(legacyMessages);
27554
- }
27555
- const nativeCodexMessages = shouldFilter ? filterDisplayMessages(parseCodexAspTranscript(codexAspTranscript), agentType) : parseCodexAspTranscript(codexAspTranscript);
27556
- return applySyntheticNotices(mergeCodexAspDisplayMessages(nativeCodexMessages, legacyMessages));
27566
+ function normalizeAutomationGithubCheckNames(names) {
27567
+ return names.map((name) => name.trim()).filter(Boolean);
27557
27568
  }
27558
- function applyInterruptions(messages, events) {
27559
- const result = [...messages];
27560
- for (const event of events) {
27561
- if (event.type !== CHAT_INTERRUPTED_EVENT_TYPE) continue;
27562
- insertDisplayMessageByTimestamp(result, {
27563
- id: `interruption-${event.timestamp}`,
27564
- type: "interruption",
27565
- timestamp: event.timestamp
27566
- });
27569
+
27570
+ // ../shared/src/automations/agent-validation.ts
27571
+ function validateAgentSelection(body, existing, modelCatalogs = {}) {
27572
+ const agentProvider = body.agent_provider;
27573
+ if (agentProvider !== void 0 && agentProvider !== null) {
27574
+ if (!isValidAgentProvider(agentProvider)) {
27575
+ return createErrorResult({ message: `Invalid agent_provider: must be one of ${VALID_AGENT_PROVIDERS.join(", ")}` });
27576
+ }
27567
27577
  }
27568
- let lastUserMs = null;
27569
- let lastInterruption = null;
27570
- const finalized = [];
27571
- for (const msg of result) {
27572
- if (msg.type === "user") {
27573
- lastUserMs = parseTimestampMs(msg.timestamp);
27574
- lastInterruption = null;
27575
- finalized.push(msg);
27576
- continue;
27578
+ const effectiveAgent = agentProvider !== void 0 ? agentProvider : existing.agentProvider;
27579
+ const agentChanged = agentProvider !== void 0 && agentProvider !== existing.agentProvider;
27580
+ let model = body.model;
27581
+ if (agentChanged && model === void 0 && existing.model) {
27582
+ model = null;
27583
+ }
27584
+ if (model !== void 0 && model !== null) {
27585
+ if (typeof model !== "string" || model.trim().length === 0) {
27586
+ return createErrorResult({ message: "model must be a non-empty string" });
27577
27587
  }
27578
- if (msg.type !== "interruption") {
27579
- finalized.push(msg);
27580
- continue;
27588
+ model = normalizeClaudeModel(model) ?? model;
27589
+ if (!effectiveAgent) {
27590
+ return createErrorResult({ message: "Cannot set model without an agent_provider (the model would be applied against the org default agent and may not be valid for it)" });
27581
27591
  }
27582
- const ms = parseTimestampMs(msg.timestamp);
27583
- if (lastInterruption && ms - parseTimestampMs(lastInterruption.timestamp) <= INTERRUPTION_DEDUP_WINDOW_MS) {
27584
- continue;
27592
+ const allowed = getAgentModelOptions(effectiveAgent, modelCatalogs);
27593
+ if (!isAgentModelAllowed(effectiveAgent, model, modelCatalogs)) {
27594
+ return createErrorResult({ message: `Invalid model "${model}" for agent ${effectiveAgent}. Valid models: ${allowed.join(", ")}` });
27585
27595
  }
27586
- lastInterruption = {
27587
- ...msg,
27588
- ...lastUserMs !== null && ms >= lastUserMs ? { durationMs: ms - lastUserMs } : {}
27589
- };
27590
- finalized.push(lastInterruption);
27591
27596
  }
27592
- return finalized;
27593
- }
27594
- function applyAuthFallbackNotices(messages, events) {
27595
- const notices = [];
27596
- events.forEach((event, index) => {
27597
- if (event.type !== AUTH_FALLBACK_EVENT_TYPE) return;
27598
- const payload = coerceAuthFallbackPayload(event.payload);
27599
- if (!payload) return;
27600
- notices.push({
27601
- ...payload,
27602
- id: `auth-fallback-${event.timestamp}-${index}`,
27603
- type: "auth_fallback",
27604
- timestamp: event.timestamp
27605
- });
27606
- });
27607
- if (notices.length === 0) return messages;
27608
- const result = [...messages];
27609
- for (const notice of notices) insertDisplayMessageByTimestamp(result, notice);
27610
- return result;
27611
- }
27612
- function isCodexInitializationPrompt(message) {
27613
- return message.type === "user" && removeReplicasInstructions(message.content).trim() === "Hello";
27614
- }
27615
- function filterDisplayMessages(messages, provider) {
27616
- let result = messages;
27617
- if (provider === "codex") {
27618
- const userMessages = result.map((msg, index) => ({ msg, index })).filter(({ msg }) => msg.type === "user");
27619
- if (userMessages.length >= 2 && isCodexInitializationPrompt(userMessages[0].msg)) {
27620
- result = result.slice(userMessages[1].index);
27597
+ const thinkingLevel = body.thinking_level;
27598
+ if (thinkingLevel !== void 0 && thinkingLevel !== null) {
27599
+ if (!effectiveAgent && isProviderSpecificThinkingLevel(thinkingLevel)) {
27600
+ return createErrorResult({ message: `Cannot set ${thinkingLevel} without an agent_provider` });
27621
27601
  }
27622
- }
27623
- result = result.map((msg) => {
27624
- if (msg.type !== "user") return msg;
27625
- const cleaned = removeReplicasInstructions(msg.content).trim();
27626
- if (INTERRUPTED_MESSAGE_REGEX.test(cleaned)) {
27627
- return { id: msg.id, type: "interruption", timestamp: msg.timestamp };
27602
+ if (!isValidThinkingLevel(thinkingLevel) || effectiveAgent && !getThinkingLevelOptions(effectiveAgent).includes(thinkingLevel)) {
27603
+ const validLevels = effectiveAgent ? getThinkingLevelOptions(effectiveAgent) : VALID_THINKING_LEVELS;
27604
+ return createErrorResult({ message: `Invalid thinking_level: must be one of ${validLevels.join(", ")}` });
27628
27605
  }
27629
- return cleaned !== msg.content ? { ...msg, content: cleaned } : msg;
27630
- });
27631
- return result;
27606
+ }
27607
+ return createSuccessResult({ agentProvider, model, thinkingLevel });
27632
27608
  }
27633
27609
 
27610
+ // ../shared/src/automations/github/index.ts
27611
+ var REPO_FILTER_PARAM = {
27612
+ id: "repository_ids",
27613
+ label: "Filter by repository",
27614
+ type: "repository_ids"
27615
+ };
27616
+ var EXCLUDED_ACTORS_PARAM = {
27617
+ id: "excluded_actors",
27618
+ label: "Exclude users",
27619
+ type: "excluded_actors"
27620
+ };
27621
+ var GROUP_PR_EVENTS_PARAM = {
27622
+ id: "group_pr_events",
27623
+ label: "Group PR events to same workspace",
27624
+ type: "boolean"
27625
+ };
27626
+ var GITHUB_TRIGGER = {
27627
+ type: "github",
27628
+ label: "GitHub",
27629
+ description: "Trigger on repository events",
27630
+ icon: "github",
27631
+ events: [
27632
+ { id: "pull_request.opened", label: "PR opened", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM, GROUP_PR_EVENTS_PARAM] },
27633
+ { id: "pull_request.synchronize", label: "PR updated (new commits)", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM, GROUP_PR_EVENTS_PARAM] },
27634
+ { id: "pull_request.merged", label: "PR merged", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM] },
27635
+ { id: "pull_request.closed", label: "PR closed", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM] },
27636
+ { id: "pull_request.command", label: "PR command (/replicas run)", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM, GROUP_PR_EVENTS_PARAM] }
27637
+ ]
27638
+ };
27639
+
27640
+ // ../shared/src/automations/gitlab/index.ts
27641
+ var REPO_FILTER_PARAM2 = {
27642
+ id: "repository_ids",
27643
+ label: "Filter by project",
27644
+ type: "repository_ids"
27645
+ };
27646
+ var EXCLUDED_ACTORS_PARAM2 = {
27647
+ id: "excluded_actors",
27648
+ label: "Exclude users",
27649
+ type: "excluded_actors"
27650
+ };
27651
+ var GROUP_MR_EVENTS_PARAM = {
27652
+ id: "group_pr_events",
27653
+ label: "Group MR events to same workspace",
27654
+ type: "boolean"
27655
+ };
27656
+ var GITLAB_TRIGGER = {
27657
+ type: "gitlab",
27658
+ label: "GitLab",
27659
+ description: "Trigger on project events",
27660
+ icon: "gitlab",
27661
+ events: [
27662
+ { id: "merge_request.opened", label: "MR opened", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2, GROUP_MR_EVENTS_PARAM] },
27663
+ { id: "merge_request.updated", label: "MR updated (new commits)", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2, GROUP_MR_EVENTS_PARAM] },
27664
+ { id: "merge_request.merged", label: "MR merged", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2] },
27665
+ { id: "merge_request.closed", label: "MR closed", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2] }
27666
+ ]
27667
+ };
27668
+
27669
+ // ../shared/src/routes/repo-files.ts
27670
+ var MAX_REPO_FILE_CONTENT_BYTES = 256 * 1024;
27671
+
27672
+ // ../shared/src/routes/admin.ts
27673
+ var WEBHOOK_PROVIDERS = ["linear", "github", "gitlab", "slack", "e2b", "stripe", "sentry"];
27674
+ var WEBHOOK_PROVIDER_SET = new Set(WEBHOOK_PROVIDERS);
27675
+
27634
27676
  // ../shared/src/onboarding-emails.ts
27635
27677
  var EMAIL_KEYS = [
27636
27678
  "welcome",