replicas-cli 0.2.602 → 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 +1921 -1855
  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.
@@ -23490,7 +23648,7 @@ If none of these apply, don't upload.
23490
23648
  | Slack | \`files.getUploadURLExternal\` \u2192 raw-byte POST \u2192 \`files.completeUploadExternal\` | Uploaded file in the channel or thread |
23491
23649
  | Linear | \`fileUpload\` mutation \u2192 presigned upload \u2192 issue/comment mutation | Returned \`assetUrl\` in Markdown |
23492
23650
  | GitLab | Project \`/uploads\` endpoint with the original bytes | Returned GitLab Markdown |
23493
- | GitHub | No supported attachment API; use a Replicas public forge share for the inline image or video poster | Printed Forge embed, linked to the recording when needed |
23651
+ | GitHub | \`gh\` v2.99.0+ \`--attach\` for images and videos; use a Replicas public forge share as the fallback | Native GitHub attachment URL, or a Forge embed fallback |
23494
23652
 
23495
23653
  The dashboard link is the subtitle and jump-back path. It is never a substitute for the destination upload.
23496
23654
 
@@ -23521,7 +23679,7 @@ Names are normalized to hyphen-case (spaces become hyphens, e.g. \`Login page\`
23521
23679
  For each media file, the CLI prints an embed line and a dashboard link:
23522
23680
 
23523
23681
  1. Default and organization access print \`Replicas chat embed: ![filename](<api-url>)\`; this authenticated URL renders inline in Replicas chat **only** \u2014 see below.
23524
- 2. Public access (\`--access public\` or \`--share\`) prints \`![filename](<public-url>)\` as the Forge embed instead. This is the public, stable bearer URL to paste into GitHub.
23682
+ 2. Public access (\`--access public\` or \`--share\`) prints \`![filename](<public-url>)\` as the Forge embed instead. This is the public, stable bearer URL for GitHub fallback embeds when native \`--attach\` is unavailable and sharing is authorized.
23525
23683
  3. \`View in Replicas: <deep-link>\` is a per-file dashboard URL of the form \`https://replicas.dev/workspaces/<workspace-id>?media=<media-id>\` that opens directly to that specific file.
23526
23684
 
23527
23685
  Match each "View in Replicas" line to the embed line directly above it \u2014 that's the deep link for that file.
@@ -23550,7 +23708,7 @@ replicas media upload screenshot.png --name "Login page" --access public
23550
23708
  |---|---|---|
23551
23709
  | \`https://api.replicas.dev/v1/media/<id>\` (inside \`![\u2026](\u2026)\`) | Authenticated API redirect to a presigned blob \u2014 requires the Replicas chat session | **Only** in the Replicas chat reply. Nowhere else. |
23552
23710
  | \`https://replicas.dev/organizations/<organization-id>/media/<media-id>\` | Organization-only page printed by \`--access organization\` | Pull requests and other places where every viewer belongs to the Replicas organization. |
23553
- | \`https://api.replicas.dev/v1/media/public/<id>/<token>\` | Opt-in public bearer URL printed by \`--access public\` or \`--share\` | The Replicas chat reply and GitHub embeds, for image, video, or audio media. |
23711
+ | \`https://api.replicas.dev/v1/media/public/<id>/<token>\` | Opt-in public bearer URL printed by \`--access public\` or \`--share\` | The Replicas chat reply and GitHub fallback embeds, for image, video, or audio media. |
23554
23712
  | \`https://replicas.dev/workspaces/<workspace-id>?media=<media-id>\` | Authenticated dashboard deep link that opens the media file in the workspace | Everywhere external: Slack, GitHub, GitLab, Linear, PR bodies, comments, emails. |
23555
23713
 
23556
23714
  ### NEVER link the API redirect URL outside the Replicas chat reply
@@ -23594,7 +23752,7 @@ After \`replicas media upload\` succeeds, external delivery has two required fol
23594
23752
 
23595
23753
  A link alone is not enough. An upload without the dashboard link is not enough. Do both.
23596
23754
 
23597
- Slack, Linear, and GitLab receive the original bytes through their native upload APIs. GitHub has no supported attachment API, so use an explicit Replicas public share for screenshots and recording previews.
23755
+ Slack, Linear, and GitLab receive the original bytes through their native upload APIs. GitHub image and video attachments use \`gh --attach\` when the installed CLI and host support it; use an explicit Replicas public share only as the fallback.
23598
23756
 
23599
23757
  ### Slack \u2014 embed natively via the 3-step external file upload flow
23600
23758
 
@@ -23706,26 +23864,50 @@ The same flow works for issue bodies (use \`issueCreate\` / \`issueUpdate\` with
23706
23864
 
23707
23865
  Never paste the \`api.replicas.dev/v1/media/<id>\` URL into a Linear body or comment \u2014 it will render as a broken image to anyone outside your Replicas chat.
23708
23866
 
23709
- ### GitHub: inline screenshots and recording previews
23867
+ ### GitHub: native image and video attachments
23868
+
23869
+ GitHub CLI v2.99.0+ supports a repeatable \`--attach\` flag on \`gh pr create\`, \`gh pr edit\`, and \`gh pr comment\` for GitHub.com and GitHub Enterprise Cloud. It uploads local images and videos to GitHub and writes the resulting attachment URL into the PR body or comment; it requires push access to the repository. GitHub Enterprise Server and unsupported media kinds are not covered by this path. See [GitHub's attachment guide](https://docs.github.com/en/github-cli/github-cli/attaching-files-with-github-cli).
23870
+
23871
+ Check \`gh --version\` before choosing the delivery path. On a supported host with v2.99.0 or later:
23872
+
23873
+ 1. Run \`replicas media upload\` with the original file first, without \`--share\`. Keep the matching **View in Replicas** dashboard link for the PR.
23874
+ 2. Put each local file path in the Markdown body where the attachment should appear, and place its dashboard link next to it.
23875
+ 3. Pass the same original path once with \`--attach\`. GitHub CLI rewrites a matching local reference in place, preserving its alt text; an unreferenced attachment is appended to the body.
23876
+
23877
+ \`\`\`markdown
23878
+ Before the change:
23710
23879
 
23711
- GitHub has **no public API** for uploading attachments to PRs or issues. Its web UI uses an undocumented internal endpoint that API clients must not emulate. For screenshots, explicitly create a forge share and put the printed \`Forge embed\` Markdown directly in the PR body or comment, followed by the dashboard link:
23880
+ ![Before state](/tmp/before.png)
23881
+ [View in Replicas](https://replicas.dev/workspaces/<workspace-id>?media=<before-media-id>)
23882
+ \`\`\`
23712
23883
 
23713
23884
  \`\`\`bash
23714
- replicas media upload /tmp/screenshot.png --name "PR screenshot" --share
23885
+ gh pr create \\
23886
+ --title "Describe the change" \\
23887
+ --body-file /tmp/pr-body.md \\
23888
+ --attach /tmp/before.png
23889
+
23890
+ gh pr edit 456 --body-file /tmp/pr-body.md --attach /tmp/after.png
23891
+ gh pr comment 456 --body-file /tmp/comment.md --attach /tmp/result.mp4
23715
23892
  \`\`\`
23716
23893
 
23717
- GitHub does not reliably render arbitrary external videos as inline players. For a recording:
23894
+ Repeat \`--attach\` for multiple files, up to 50 per command. For an appended image, add alt text after the path with \`#\`, for example \`--attach './error.png#Error state'\`. To render a video as a player, its local Markdown reference must be the only content in its paragraph. Do not paste a Replicas authenticated API URL into the body, and do not emulate GitHub's undocumented browser upload endpoint.
23895
+
23896
+ ### GitHub attachment fallback
23897
+
23898
+ If \`--attach\` is unavailable, the target is GitHub Enterprise Server, the file is not an image or video, or the native upload fails, keep the Replicas dashboard link and report the limitation. For an inline screenshot or video poster, create a Replicas public forge share only for a public repository or after explicit consent to publish media from a private repository, then use the printed \`Forge embed\` Markdown:
23899
+
23900
+ \`\`\`bash
23901
+ replicas media upload /tmp/screenshot.png --name "PR screenshot" --share
23902
+ \`\`\`
23718
23903
 
23719
- 1. Upload the MP4 normally so it remains available in Replicas.
23720
- 2. Generate a poster image from the recording: \`ffmpeg -i /tmp/demo.mp4 -frames:v 1 /tmp/demo-poster.png\`.
23721
- 3. Upload the poster with \`--share\`.
23722
- 4. Wrap the poster embed in a link to the recording's **View in Replicas** URL.
23904
+ For a fallback recording preview, upload the MP4 normally, generate a poster with \`ffmpeg -i /tmp/demo.mp4 -frames:v 1 /tmp/demo-poster.png\`, share the poster, and link its embed to the recording's **View in Replicas** URL:
23723
23905
 
23724
23906
  \`\`\`markdown
23725
23907
  [![Demo recording](<public-poster-url>)](<recording-dashboard-url>)
23726
23908
  \`\`\`
23727
23909
 
23728
- Public forge shares are unauthenticated bearer capabilities. For private repositories, create one only when the user explicitly asked for inline PR media. Without that consent, keep the media private in Replicas and report that GitHub has no supported attachment API; do not publish a bearer share or pretend the dashboard link is an embed. Revoke with \`replicas media revoke <media-id>\` **only** when the user asks or the media should intentionally stop rendering; never as routine post-task cleanup, since revoking breaks every embed already posted on the PR.
23910
+ Public forge shares are unauthenticated bearer capabilities. Revoke with \`replicas media revoke <media-id>\` **only** when the user asks or the media should intentionally stop rendering; never as routine post-task cleanup, since revoking breaks every embed already posted on the PR.
23729
23911
 
23730
23912
  For a self-contained HTML report, upload it with \`--access organization\` and put the printed \`Forge link\` Markdown in the PR body or comment. GitHub cannot render the page inline, so use the normal link rather than image syntax. HTML pages cannot be shared publicly.
23731
23913
 
@@ -24500,7 +24682,9 @@ var DEFAULT_USER_PREFERENCES = {
24500
24682
  open_links_in_desktop_app: false,
24501
24683
  default_fast_mode: false,
24502
24684
  agent_defaults: { ...EMPTY_AGENT_DEFAULT_SETTINGS },
24503
- credential_priority: null
24685
+ credential_priority: null,
24686
+ chat_fork_fallbacks_enabled: false,
24687
+ chat_fork_fallback_providers: []
24504
24688
  };
24505
24689
  function defaultReplicasAgentAbilities() {
24506
24690
  const abilities = {};
@@ -24530,980 +24714,24 @@ function removeReplicasInstructions(text) {
24530
24714
  return removeTag(text, REPLICAS_INSTRUCTIONS_TAG);
24531
24715
  }
24532
24716
 
24533
- // ../shared/src/replicas-config.ts
24534
- var import_yaml = __toESM(require_dist());
24535
-
24536
- // ../shared/src/warm-hooks.ts
24537
- var DEFAULT_WARM_HOOK_TIMEOUT_MS = 60 * 60 * 1e3;
24538
- var MAX_WARM_HOOK_TIMEOUT_MS = 120 * 60 * 1e3;
24539
- var DEFAULT_START_HOOK_TIMEOUT_MS = 5 * 60 * 1e3;
24540
- var HOOK_EXEC_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
24541
-
24542
- // ../shared/src/replicas-config.ts
24543
- var REPLICAS_CONFIG_FILENAMES = ["replicas.json", "replicas.yaml", "replicas.yml"];
24544
-
24545
- // ../shared/src/headless-agent.ts
24546
- var headlessAgentRequestBaseSchema = external_exports.object({
24547
- agent: external_exports.custom(
24548
- (value) => typeof value === "string" && isValidCodingAgentProvider(value)
24549
- ),
24550
- model: external_exports.string().min(1),
24551
- prompt: external_exports.string(),
24552
- workingDirectory: external_exports.string().min(1),
24553
- timeoutSeconds: external_exports.number().int().min(1).max(1800).default(300),
24554
- codexOauthTokens: external_exports.object({ accessToken: external_exports.string(), accountId: external_exports.string() }).optional()
24555
- });
24556
- var headlessAgentInputFileSchema = external_exports.object({
24557
- path: external_exports.string().min(1),
24558
- downloadUrl: external_exports.url()
24559
- });
24560
- var headlessAgentOutputFileSchema = external_exports.object({
24561
- path: external_exports.string().min(1),
24562
- uploadUrl: external_exports.url(),
24563
- contentType: external_exports.string().min(1),
24564
- minChars: external_exports.number().int().positive().optional(),
24565
- maxChars: external_exports.number().int().positive().optional()
24566
- });
24567
- var headlessAgentRequestSchema = external_exports.discriminatedUnion("mode", [
24568
- headlessAgentRequestBaseSchema.extend({
24569
- mode: external_exports.literal("structured"),
24570
- outputSchema: external_exports.record(external_exports.string(), external_exports.json())
24571
- }),
24572
- headlessAgentRequestBaseSchema.extend({
24573
- mode: external_exports.literal("filesystem"),
24574
- inputFiles: external_exports.array(headlessAgentInputFileSchema),
24575
- outputFiles: external_exports.array(headlessAgentOutputFileSchema),
24576
- sensitiveValues: external_exports.array(external_exports.string())
24577
- })
24578
- ]);
24579
- var headlessFilesystemAgentResultSchema = external_exports.object({
24580
- message: external_exports.string(),
24581
- files: external_exports.record(external_exports.string(), external_exports.object({
24582
- sha256: external_exports.string().regex(/^[a-f0-9]{64}$/),
24583
- sizeBytes: external_exports.number().int().nonnegative()
24584
- }))
24585
- });
24586
-
24587
- // ../shared/src/errors.ts
24588
- var TRANSIENT_NETWORK_ERROR_PATTERNS = [
24589
- /socket connection was closed unexpectedly/,
24590
- /fetch failed/,
24591
- /network error/,
24592
- /network request failed/,
24593
- /econnreset/,
24594
- /econnrefused/,
24595
- /etimedout/,
24596
- /enotfound/,
24597
- /eai_again/,
24598
- /und_err_socket/,
24599
- /und_err_connect_timeout/,
24600
- /headers timeout/,
24601
- /body timeout/,
24602
- /connection reset/,
24603
- /connection closed/,
24604
- /closed unexpectedly/
24605
- ];
24606
- var HTTP_CONTEXT_PATTERN = /(api error|request|response|status|http)/;
24607
- var HTTP_STATUS_PATTERN = /(^|[^0-9])([1-5][0-9]{2})(?=$|[^0-9])/g;
24608
- function isTransientErrorText(text, options = {}) {
24609
- const normalized = text.toLowerCase();
24610
- if (!normalized) return false;
24611
- if (TRANSIENT_NETWORK_ERROR_PATTERNS.some((pattern) => pattern.test(normalized))) return true;
24612
- if (options.includeRateLimit && /rate limit/.test(normalized)) return true;
24613
- if (options.extraPatterns?.some((pattern) => pattern.test(normalized))) return true;
24614
- const httpStatuses = new Set(options.httpStatuses ?? []);
24615
- const httpStatusClasses = new Set(options.httpStatusClasses ?? []);
24616
- if (httpStatuses.size === 0 && httpStatusClasses.size === 0) return false;
24617
- if (options.requireHttpContext !== false && !HTTP_CONTEXT_PATTERN.test(normalized)) return false;
24618
- for (const match of normalized.matchAll(HTTP_STATUS_PATTERN)) {
24619
- const status = Number(match[2]);
24620
- if (httpStatuses.has(status) || httpStatusClasses.has(Math.floor(status / 100))) return true;
24621
- }
24622
- return false;
24717
+ // ../shared/src/display-message/parsers/codex-parser.ts
24718
+ function getStatusFromExitCode(exitCode) {
24719
+ return exitCode === 0 ? "completed" : "failed";
24623
24720
  }
24624
-
24625
- // ../shared/src/cli-version.ts
24626
- var CLI_VERSION = "0.2.602";
24627
-
24628
- // ../shared/src/version.ts
24629
- function compareVersions(v1, v2) {
24630
- const parts1 = v1.split(".").map(Number);
24631
- const parts2 = v2.split(".").map(Number);
24632
- for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
24633
- const num1 = parts1[i] || 0;
24634
- const num2 = parts2[i] || 0;
24635
- if (num1 > num2) return 1;
24636
- if (num1 < num2) return -1;
24637
- }
24638
- return 0;
24721
+ function getPayloadString(event, key) {
24722
+ const value = event.payload?.[key];
24723
+ return typeof value === "string" ? value : null;
24639
24724
  }
24640
-
24641
- // ../shared/src/engine/v1.ts
24642
- var createChatRequestSchema = external_exports.object({
24643
- id: external_exports.string().uuid().optional(),
24644
- createdAt: external_exports.string().datetime().optional(),
24645
- provider: external_exports.enum(VALID_AGENT_PROVIDERS),
24646
- title: external_exports.string().min(1).optional(),
24647
- parentChatId: external_exports.string().uuid().optional(),
24648
- clientRequestId: external_exports.string().min(1).max(128).optional()
24649
- });
24650
- var sendChatMessageRequestSchema = external_exports.object({
24651
- messageId: external_exports.string().min(1).optional(),
24652
- submittedAt: external_exports.string().datetime().optional(),
24653
- message: external_exports.string().min(1),
24654
- model: external_exports.string().optional(),
24655
- customInstructions: external_exports.string().optional(),
24656
- planMode: external_exports.boolean().optional(),
24657
- images: external_exports.array(external_exports.object({
24658
- type: external_exports.literal("image"),
24659
- source: external_exports.discriminatedUnion("type", [
24660
- external_exports.object({ type: external_exports.literal("base64"), media_type: external_exports.enum(IMAGE_MEDIA_TYPES), data: external_exports.string().min(1) }),
24661
- external_exports.object({ type: external_exports.literal("url"), url: external_exports.string().url() })
24662
- ])
24663
- })).optional(),
24664
- canvasUploadIds: external_exports.array(external_exports.string().min(1)).optional(),
24665
- thinkingLevel: external_exports.enum(VALID_THINKING_LEVELS).optional(),
24666
- goalMode: external_exports.boolean().optional(),
24667
- fastMode: external_exports.boolean().optional(),
24668
- enableInteractiveTools: external_exports.boolean().optional(),
24669
- type: external_exports.string().min(1).optional(),
24670
- merge: external_exports.boolean().optional(),
24671
- idempotencyKey: external_exports.string().min(1).max(128).optional(),
24672
- automationId: external_exports.string().optional(),
24673
- environmentId: external_exports.string().min(1).optional(),
24674
- senderUserId: external_exports.string().optional(),
24675
- senderEmail: external_exports.string().optional(),
24676
- senderDisplayName: external_exports.string().optional(),
24677
- senderAvatarUrl: external_exports.string().optional(),
24678
- errorNotificationTarget: external_exports.discriminatedUnion("type", [
24679
- external_exports.object({ type: external_exports.literal("slack") }),
24680
- external_exports.object({ type: external_exports.literal("linear"), sessionId: external_exports.string().min(1) }),
24681
- external_exports.object({
24682
- type: external_exports.literal("code_host"),
24683
- provider: external_exports.enum(["github", "gitlab"]),
24684
- resource: external_exports.enum(["issue", "pull_request"]),
24685
- repositoryId: external_exports.string().min(1),
24686
- resourceNumber: external_exports.number().int().positive()
24687
- }),
24688
- external_exports.object({ type: external_exports.literal("automation"), executionId: external_exports.string().min(1) })
24689
- ]).optional()
24690
- }).passthrough();
24691
- function normalizeCodexAspTranscriptStatus(status, failed = false) {
24692
- if (failed || status === "failed" || status === "declined") return "failed";
24693
- if (status === "completed") return "completed";
24694
- 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}`;
24695
24728
  }
24696
- function getCodexAspTurnResponse(turn) {
24697
- const finalAnswer = turn.items.find((item) => item.type === "agentMessage" && item.phase === "final_answer");
24698
- if (finalAnswer) return finalAnswer;
24699
- if (normalizeCodexAspTranscriptStatus(turn.status) === "in_progress") return null;
24700
- return turn.items.findLast((item) => item.type === "agentMessage") ?? null;
24701
- }
24702
-
24703
- // ../shared/src/routes/plugin-catalog.ts
24704
- var EXPLICIT_PLUGIN_CATALOG = [
24705
- {
24706
- id: "attio",
24707
- backend: "composio",
24708
- toolkit: "attio",
24709
- authType: "oauth",
24710
- category: "business",
24711
- name: "Attio",
24712
- description: "Manage CRM records, people, companies, lists, and notes."
24713
- },
24714
- {
24715
- id: "calendly",
24716
- backend: "composio",
24717
- toolkit: "calendly",
24718
- authType: "oauth",
24719
- category: "productivity",
24720
- name: "Calendly",
24721
- description: "Manage event types, scheduled events, invitees, and scheduling links."
24722
- },
24723
- {
24724
- id: "clickhouse",
24725
- backend: "composio",
24726
- toolkit: "clickhouse",
24727
- authType: "basic",
24728
- category: "data",
24729
- name: "ClickHouse",
24730
- description: "Manage databases, tables, schemas, and queries."
24731
- },
24732
- {
24733
- id: "cloudflare",
24734
- backend: "composio",
24735
- toolkit: "cloudflare_api_key",
24736
- authType: "api_key",
24737
- category: "data",
24738
- name: "Cloudflare",
24739
- description: "Manage zones, DNS records, analytics, and security settings."
24740
- },
24741
- {
24742
- id: "e2b",
24743
- toolkit: "e2b",
24744
- backend: "native",
24745
- authType: "api_key",
24746
- category: "data",
24747
- name: "E2B",
24748
- description: "Create secure cloud sandboxes and run commands in them."
24749
- },
24750
- {
24751
- id: "gmail",
24752
- backend: "composio",
24753
- toolkit: "gmail",
24754
- authType: "oauth",
24755
- category: "productivity",
24756
- name: "Gmail",
24757
- description: "Manage messages, threads, attachments, labels, and mailbox settings."
24758
- },
24759
- {
24760
- id: "googleads",
24761
- backend: "composio",
24762
- toolkit: "googleads",
24763
- authType: "oauth",
24764
- category: "data",
24765
- name: "Google Ads",
24766
- description: "Manage campaigns, customers, accounts, and reporting data."
24767
- },
24768
- {
24769
- id: "googledocs",
24770
- backend: "composio",
24771
- toolkit: "googledocs",
24772
- authType: "oauth",
24773
- category: "productivity",
24774
- name: "Google Docs",
24775
- description: "Manage documents, structure, comments, and exported content."
24776
- },
24777
- {
24778
- id: "googledrive",
24779
- backend: "composio",
24780
- toolkit: "googledrive",
24781
- authType: "oauth",
24782
- category: "productivity",
24783
- name: "Google Drive",
24784
- description: "Manage files, folders, metadata, permissions, and shared drives."
24785
- },
24786
- {
24787
- id: "googlesearch",
24788
- backend: "composio",
24789
- toolkit: "google_search_console",
24790
- authType: "oauth",
24791
- category: "data",
24792
- name: "Google Search",
24793
- description: "Manage Search Console performance, indexing, sites, and sitemaps."
24794
- },
24795
- {
24796
- id: "googlesheets",
24797
- backend: "composio",
24798
- toolkit: "googlesheets",
24799
- authType: "oauth",
24800
- category: "productivity",
24801
- name: "Google Sheets",
24802
- description: "Manage spreadsheets, worksheets, ranges, tables, and charts."
24803
- },
24804
- {
24805
- id: "linkedin",
24806
- backend: "composio",
24807
- toolkit: "linkedin",
24808
- authType: "oauth",
24809
- category: "business",
24810
- name: "LinkedIn",
24811
- description: "Manage profiles, organizations, posts, and professional activity."
24812
- },
24813
- {
24814
- id: "modal",
24815
- backend: "native",
24816
- toolkit: "modal",
24817
- authType: "basic",
24818
- category: "data",
24819
- name: "Modal",
24820
- description: "Invoke deployed functions through Modal compute."
24821
- },
24822
- {
24823
- id: "posthog",
24824
- backend: "composio",
24825
- toolkit: "posthog",
24826
- authType: "api_key",
24827
- category: "data",
24828
- name: "PostHog",
24829
- description: "Manage projects, events, insights, dashboards, flags, and recordings."
24830
- },
24831
- {
24832
- id: "planetscale",
24833
- toolkit: "planetscale",
24834
- backend: "native",
24835
- authType: "oauth",
24836
- category: "data",
24837
- name: "PlanetScale",
24838
- description: "Manage organizations, databases, branches, deploy requests, and Insights."
24839
- },
24840
- {
24841
- id: "stripe",
24842
- backend: "composio",
24843
- toolkit: "stripe",
24844
- authType: "oauth",
24845
- category: "business",
24846
- name: "Stripe",
24847
- description: "Manage customers, payments, invoices, subscriptions, and balances."
24848
- },
24849
- {
24850
- id: "pangram",
24851
- backend: "native",
24852
- toolkit: "pangram",
24853
- authType: "api_key",
24854
- category: "data",
24855
- name: "Pangram",
24856
- description: "Analyze text for AI generation and plagiarism signals."
24857
- },
24858
- {
24859
- id: "vercel",
24860
- backend: "native",
24861
- toolkit: "vercel",
24862
- authType: "api_key",
24863
- category: "data",
24864
- name: "Vercel",
24865
- description: "Manage projects, deployments, logs, domains, and infrastructure."
24866
- },
24867
- {
24868
- id: "turbopuffer",
24869
- backend: "native",
24870
- toolkit: "turbopuffer",
24871
- authType: "api_key",
24872
- category: "data",
24873
- name: "turbopuffer",
24874
- description: "Query and manage turbopuffer namespaces."
24875
- },
24876
- {
24877
- id: "vanta",
24878
- backend: "native",
24879
- toolkit: "vanta",
24880
- authType: "credentials",
24881
- category: "data",
24882
- name: "Vanta",
24883
- description: "Review and manage controls, tests, evidence, vendors, risks, and compliance data.",
24884
- scopes: ["environment"]
24885
- }
24886
- ];
24887
- var HOSTED_COMPOSIO_PLUGIN_DEFINITIONS = [
24888
- ["notion", "notion", "OAUTH2", true, "productivity", "Notion"],
24889
- ["jira", "jira", "OAUTH2", true, "productivity", "Jira"],
24890
- ["confluence", "confluence", "OAUTH2", true, "productivity", "Confluence"],
24891
- ["googlecalendar", "googlecalendar", "OAUTH2", true, "productivity", "Google Calendar"],
24892
- ["microsoftteams", "microsoft_teams", "OAUTH2", true, "productivity", "Microsoft Teams"],
24893
- ["outlook", "outlook", "OAUTH2", true, "productivity", "Outlook"],
24894
- ["bitbucket", "bitbucket", "OAUTH2", true, "data", "Bitbucket"],
24895
- ["datadog", "datadog", "API_KEY", false, "data", "Datadog"],
24896
- ["pagerduty", "pagerduty", "OAUTH2", true, "data", "PagerDuty"],
24897
- ["intercom", "intercom", "OAUTH2", true, "business", "Intercom"],
24898
- ["zendesk", "zendesk", "OAUTH2", true, "business", "Zendesk"],
24899
- ["hubspot", "hubspot", "OAUTH2", true, "business", "HubSpot"],
24900
- ["supabase", "supabase", "OAUTH2", true, "data", "Supabase"],
24901
- ["figma", "figma", "OAUTH2", true, "productivity", "Figma"],
24902
- ["launchdarkly", "launch_darkly", "API_KEY", false, "data", "LaunchDarkly"],
24903
- ["asana", "asana", "OAUTH2", true, "productivity", "Asana"],
24904
- ["clickup", "clickup", "OAUTH2", true, "productivity", "ClickUp"],
24905
- ["trello", "trello", "OAUTH1", true, "productivity", "Trello"],
24906
- ["todoist", "todoist", "OAUTH2", true, "productivity", "Todoist"],
24907
- ["airtable", "airtable", "OAUTH2", true, "productivity", "Airtable"],
24908
- ["coda", "coda", "API_KEY", false, "productivity", "Coda"],
24909
- ["miro", "miro", "OAUTH2", true, "productivity", "Miro"],
24910
- ["sharepoint", "share_point", "OAUTH2", true, "productivity", "SharePoint"],
24911
- ["onedrive", "one_drive", "OAUTH2", true, "productivity", "OneDrive"],
24912
- ["googleslides", "googleslides", "OAUTH2", true, "productivity", "Google Slides"],
24913
- ["googlemeet", "googlemeet", "OAUTH2", true, "productivity", "Google Meet"],
24914
- ["googletasks", "googletasks", "OAUTH2", true, "productivity", "Google Tasks"],
24915
- ["dropbox", "dropbox", "OAUTH2", true, "productivity", "Dropbox"],
24916
- ["box", "box", "OAUTH2", true, "productivity", "Box"],
24917
- ["discord", "discord", "OAUTH2", true, "productivity", "Discord"],
24918
- ["discordbot", "discordbot", "OAUTH2", true, "productivity", "Discord Bot"],
24919
- ["zoom", "zoom", "OAUTH2", true, "productivity", "Zoom"],
24920
- ["googlechat", "google_chat", "OAUTH2", false, "productivity", "Google Chat"],
24921
- ["newrelic", "new_relic", "API_KEY", false, "data", "New Relic"],
24922
- ["betterstack", "better_stack", "API_KEY", false, "data", "Better Stack"],
24923
- ["incidentio", "incident_io", "API_KEY", false, "data", "incident.io"],
24924
- ["grafana", "grafana", "BEARER_TOKEN", false, "data", "Grafana"],
24925
- ["honeycomb", "honeycomb_mcp", "DCR_OAUTH", false, "data", "Honeycomb MCP"],
24926
- ["bugsnag", "bugsnag", "API_KEY", false, "data", "Bugsnag"],
24927
- ["circleci", "circleci", "API_KEY", false, "data", "CircleCI"],
24928
- ["buildkite", "buildkite", "API_KEY", false, "data", "Buildkite"],
24929
- ["dockerhub", "docker_hub", "API_KEY", false, "data", "Docker Hub"],
24930
- ["digitalocean", "digital_ocean", "OAUTH2", true, "data", "DigitalOcean"],
24931
- ["railway", "railway", "API_KEY", false, "data", "Railway"],
24932
- ["render", "render", "API_KEY", false, "data", "Render"],
24933
- ["firebase", "firebase", "API_KEY", false, "data", "Firebase"],
24934
- ["cloudinary", "cloudinary", "API_KEY", false, "data", "Cloudinary"],
24935
- ["configcat", "configcat", "API_KEY", false, "data", "ConfigCat"],
24936
- ["contextdev", "context_dev", "API_KEY", false, "data", "Context.dev"],
24937
- ["googleanalytics", "google_analytics", "OAUTH2", true, "data", "Google Analytics"],
24938
- ["googlebigquery", "googlebigquery", "OAUTH2", true, "data", "Google BigQuery"],
24939
- ["amplitude", "amplitude", "API_KEY", false, "data", "Amplitude"],
24940
- ["mixpanel", "mixpanel", "BASIC", false, "data", "Mixpanel"],
24941
- ["segment", "segment", "API_KEY", false, "data", "Segment"],
24942
- ["databricks", "databricks", "API_KEY", false, "data", "Databricks"],
24943
- ["snowflake", "snowflake", "OAUTH2", false, "data", "Snowflake"],
24944
- ["algolia", "algolia", "API_KEY", false, "data", "Algolia"],
24945
- ["elasticsearch", "elasticsearch", "API_KEY", false, "data", "Elasticsearch"],
24946
- ["salesforce", "salesforce", "OAUTH2", true, "business", "Salesforce"],
24947
- ["pipedrive", "pipedrive", "API_KEY", false, "business", "Pipedrive"],
24948
- ["close", "close", "API_KEY", false, "business", "Close"],
24949
- ["apollo", "apollo", "API_KEY", false, "business", "Apollo"],
24950
- ["gong", "gong", "OAUTH2", true, "business", "Gong"],
24951
- ["freshdesk", "freshdesk", "API_KEY", false, "business", "Freshdesk"],
24952
- ["helpscout", "help_scout", "OAUTH2", false, "business", "Help Scout"],
24953
- ["servicenow", "servicenow", "BASIC", false, "business", "ServiceNow"],
24954
- ["mailchimp", "mailchimp", "OAUTH2", true, "business", "Mailchimp"],
24955
- ["customerio", "customerio", "API_KEY", false, "business", "Customer.io"],
24956
- ["klaviyo", "klaviyo", "API_KEY", false, "business", "Klaviyo"],
24957
- ["shopify", "shopify", "API_KEY", false, "business", "Shopify"],
24958
- ["quickbooks", "quickbooks", "OAUTH2", true, "business", "QuickBooks"],
24959
- ["xero", "xero", "OAUTH2", false, "business", "Xero"],
24960
- ["brex", "brex", "API_KEY", false, "business", "Brex"],
24961
- ["buffer", "buffer", "OAUTH2", false, "business", "Buffer"],
24962
- ["canva", "canva", "OAUTH2", true, "productivity", "Canva"],
24963
- ["webflow", "webflow", "API_KEY", false, "business", "Webflow"],
24964
- ["firecrawl", "firecrawl", "API_KEY", false, "data", "Firecrawl"],
24965
- ["browserbase", "browserbase_tool", "API_KEY", false, "data", "Browserbase"],
24966
- ["exa", "exa", "API_KEY", false, "data", "Exa"],
24967
- ["youtube", "youtube", "OAUTH2", true, "business", "YouTube"],
24968
- ["twitter", "twitter", "OAUTH2", false, "business", "Twitter/X"],
24969
- ["instagram", "instagram", "OAUTH2", true, "business", "Instagram"],
24970
- ["facebook", "facebook", "OAUTH2", true, "business", "Facebook"]
24971
- ];
24972
- var HOSTED_COMPOSIO_PLUGINS = HOSTED_COMPOSIO_PLUGIN_DEFINITIONS.map(
24973
- ([id, toolkit, authScheme, managedAuth, category, name]) => ({
24974
- id,
24975
- backend: "composio",
24976
- toolkit,
24977
- authType: "hosted",
24978
- authScheme,
24979
- managedAuth,
24980
- category,
24981
- name,
24982
- description: `Use ${name} tools from workspace agents.`
24983
- })
24984
- );
24985
- var PLUGIN_CATALOG = [
24986
- ...EXPLICIT_PLUGIN_CATALOG,
24987
- ...HOSTED_COMPOSIO_PLUGINS
24988
- ];
24989
-
24990
- // ../shared/src/routes/user.ts
24991
- var PROFILE_AVATAR_MAX_SIZE_BYTES = 5 * 1024 * 1024;
24992
-
24993
- // ../shared/src/routes/client-state.ts
24994
- var MAX_CLIENT_STATE_REQUEST_BYTES = 5 * 1024 * 1024;
24995
- var MAX_CLIENT_STATE_BYTES_PER_USER = 10 * 1024 * 1024;
24996
-
24997
- // ../shared/src/routes/workspaces.ts
24998
- var WORKSPACE_STATUSES = ["active", "sleeping", "archived", "preparing", "error"];
24999
- function isWorkspaceSuspendedStatus(status) {
25000
- return status === "sleeping" || status === "archived";
25001
- }
25002
- function workspaceConfigWithCapabilities(config3, capabilities = {}) {
25003
- const mergedCapabilities = {
25004
- ...config3?.capabilities,
25005
- ...capabilities
25006
- };
25007
- return {
25008
- ...config3,
25009
- capabilities: {
25010
- ...mergedCapabilities,
25011
- pr_followups: mergedCapabilities.pr_followups === true
25012
- }
25013
- };
25014
- }
25015
- function workspaceConfigWithPrFollowups(config3, prFollowups = config3?.capabilities?.pr_followups === true) {
25016
- return workspaceConfigWithCapabilities(config3, { pr_followups: prFollowups });
25017
- }
25018
- var WORKSPACE_STATUS_FILTERS = WORKSPACE_STATUSES.map((status) => status === "error" ? "failed" : status);
25019
- var WORKSPACE_SORT_CHOICES = [
25020
- ["manual", "Custom"],
25021
- ["activity", "Last activity"],
25022
- ["created", "Created date"]
25023
- ];
25024
- var WORKSPACE_SORT_OPTIONS = WORKSPACE_SORT_CHOICES.map(([option]) => option);
25025
- var WORKSPACE_SORT_LABELS = Object.fromEntries(WORKSPACE_SORT_CHOICES);
25026
- var WORKSPACE_SIDEBAR_ORGANIZATION_CHOICES = [
25027
- ["chronological", "Chronological"],
25028
- ["environments", "Environments"],
25029
- ["recent", "Recent environments"]
25030
- ];
25031
- var WORKSPACE_SIDEBAR_ORGANIZATIONS = WORKSPACE_SIDEBAR_ORGANIZATION_CHOICES.map(([organization]) => organization);
25032
- var WORKSPACE_SIDEBAR_ORGANIZATION_LABELS = Object.fromEntries(WORKSPACE_SIDEBAR_ORGANIZATION_CHOICES);
25033
- var DEFAULT_STATUS_FILTERS = [...WORKSPACE_STATUS_FILTERS];
25034
- var DEFAULT_WORKSPACE_FILTERS = {
25035
- statuses: [...DEFAULT_STATUS_FILTERS],
25036
- ownerIds: [],
25037
- activity: "any"
25038
- };
25039
- var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
25040
-
25041
- // ../shared/src/routes/workspace-events.ts
25042
- var workspaceChangedEventSchema = external_exports.discriminatedUnion("type", [
25043
- external_exports.object({
25044
- type: external_exports.literal("workspace.changed"),
25045
- workspaceId: external_exports.string(),
25046
- completion: external_exports.object({
25047
- workspaceName: external_exports.string(),
25048
- completedAt: external_exports.string(),
25049
- processing: external_exports.boolean().optional()
25050
- }).optional(),
25051
- ts: external_exports.string()
25052
- }),
25053
- external_exports.object({
25054
- type: external_exports.literal("workspace.chat.changed"),
25055
- workspaceId: external_exports.string(),
25056
- chatId: external_exports.string(),
25057
- ts: external_exports.string()
25058
- }),
25059
- external_exports.object({ type: external_exports.literal("presence.changed"), ts: external_exports.string() }),
25060
- external_exports.object({
25061
- type: external_exports.literal("mobile-testing.changed"),
25062
- workspaceId: external_exports.string(),
25063
- ts: external_exports.string()
25064
- })
25065
- ]);
25066
-
25067
- // ../shared/src/routes/presence.ts
25068
- var presenceStatusSchema = external_exports.enum(["online", "typing"]);
25069
- var presenceLocationSchema = external_exports.object({
25070
- environmentId: external_exports.string().optional(),
25071
- workspaceId: external_exports.string().optional()
25072
- });
25073
- var presenceEntrySchema = external_exports.object({
25074
- userId: external_exports.string(),
25075
- status: presenceStatusSchema,
25076
- location: presenceLocationSchema,
25077
- ts: external_exports.string()
25078
- });
25079
- var updatePresenceRequestSchema = external_exports.object({
25080
- status: presenceStatusSchema,
25081
- location: presenceLocationSchema.optional()
25082
- });
25083
-
25084
- // ../shared/src/audit-log.ts
25085
- var AUDIT_LOG_ACTION = {
25086
- CREATE: "create",
25087
- UPDATE: "update",
25088
- DELETE: "delete",
25089
- CONNECT: "connect",
25090
- DISCONNECT: "disconnect"
25091
- };
25092
- var AUDIT_LOG_ACTIONS = Object.values(AUDIT_LOG_ACTION);
25093
-
25094
- // ../shared/src/automations/types.ts
25095
- var AUTOMATION_DEBOUNCE_MAX_SECONDS = 24 * 60 * 60;
25096
- var AUTOMATION_LIFECYCLE_POLICIES = ["default", "archive_when_done", "sleep_when_done"];
25097
- function toAutomationLifecyclePolicy(policy) {
25098
- if (!policy) return "default";
25099
- if (policy === "default" || policy === "archive_when_done" || policy === "sleep_when_done") return policy;
25100
- return "archive_when_done";
25101
- }
25102
- function lifecyclePolicySupportsAutoStop(policy) {
25103
- return policy === "default";
25104
- }
25105
-
25106
- // ../shared/src/automations/github-checks.ts
25107
- var AUTOMATION_CHECK_CONCLUSIONS = ["success", "failure"];
25108
- function isAutomationCheckConclusion(value) {
25109
- return AUTOMATION_CHECK_CONCLUSIONS.some((conclusion) => conclusion === value);
25110
- }
25111
- function normalizeAutomationGithubCheckNames(names) {
25112
- return names.map((name) => name.trim()).filter(Boolean);
25113
- }
25114
-
25115
- // ../shared/src/automations/agent-validation.ts
25116
- function validateAgentSelection(body, existing, modelCatalogs = {}) {
25117
- const agentProvider = body.agent_provider;
25118
- if (agentProvider !== void 0 && agentProvider !== null) {
25119
- if (!isValidAgentProvider(agentProvider)) {
25120
- return createErrorResult({ message: `Invalid agent_provider: must be one of ${VALID_AGENT_PROVIDERS.join(", ")}` });
25121
- }
25122
- }
25123
- const effectiveAgent = agentProvider !== void 0 ? agentProvider : existing.agentProvider;
25124
- const agentChanged = agentProvider !== void 0 && agentProvider !== existing.agentProvider;
25125
- let model = body.model;
25126
- if (agentChanged && model === void 0 && existing.model) {
25127
- model = null;
25128
- }
25129
- if (model !== void 0 && model !== null) {
25130
- if (typeof model !== "string" || model.trim().length === 0) {
25131
- return createErrorResult({ message: "model must be a non-empty string" });
25132
- }
25133
- model = normalizeClaudeModel(model) ?? model;
25134
- if (!effectiveAgent) {
25135
- 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)" });
25136
- }
25137
- const allowed = getAgentModelOptions(effectiveAgent, modelCatalogs);
25138
- if (!isAgentModelAllowed(effectiveAgent, model, modelCatalogs)) {
25139
- return createErrorResult({ message: `Invalid model "${model}" for agent ${effectiveAgent}. Valid models: ${allowed.join(", ")}` });
25140
- }
25141
- }
25142
- const thinkingLevel = body.thinking_level;
25143
- if (thinkingLevel !== void 0 && thinkingLevel !== null) {
25144
- if (!effectiveAgent && isProviderSpecificThinkingLevel(thinkingLevel)) {
25145
- return createErrorResult({ message: `Cannot set ${thinkingLevel} without an agent_provider` });
25146
- }
25147
- if (!isValidThinkingLevel(thinkingLevel) || effectiveAgent && !getThinkingLevelOptions(effectiveAgent).includes(thinkingLevel)) {
25148
- const validLevels = effectiveAgent ? getThinkingLevelOptions(effectiveAgent) : VALID_THINKING_LEVELS;
25149
- return createErrorResult({ message: `Invalid thinking_level: must be one of ${validLevels.join(", ")}` });
25150
- }
25151
- }
25152
- return createSuccessResult({ agentProvider, model, thinkingLevel });
25153
- }
25154
-
25155
- // ../shared/src/automations/github/index.ts
25156
- var REPO_FILTER_PARAM = {
25157
- id: "repository_ids",
25158
- label: "Filter by repository",
25159
- type: "repository_ids"
25160
- };
25161
- var EXCLUDED_ACTORS_PARAM = {
25162
- id: "excluded_actors",
25163
- label: "Exclude users",
25164
- type: "excluded_actors"
25165
- };
25166
- var GROUP_PR_EVENTS_PARAM = {
25167
- id: "group_pr_events",
25168
- label: "Group PR events to same workspace",
25169
- type: "boolean"
25170
- };
25171
- var GITHUB_TRIGGER = {
25172
- type: "github",
25173
- label: "GitHub",
25174
- description: "Trigger on repository events",
25175
- icon: "github",
25176
- events: [
25177
- { id: "pull_request.opened", label: "PR opened", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM, GROUP_PR_EVENTS_PARAM] },
25178
- { id: "pull_request.synchronize", label: "PR updated (new commits)", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM, GROUP_PR_EVENTS_PARAM] },
25179
- { id: "pull_request.merged", label: "PR merged", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM] },
25180
- { id: "pull_request.closed", label: "PR closed", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM] },
25181
- { id: "pull_request.command", label: "PR command (/replicas run)", parameters: [REPO_FILTER_PARAM, EXCLUDED_ACTORS_PARAM, GROUP_PR_EVENTS_PARAM] }
25182
- ]
25183
- };
25184
-
25185
- // ../shared/src/automations/gitlab/index.ts
25186
- var REPO_FILTER_PARAM2 = {
25187
- id: "repository_ids",
25188
- label: "Filter by project",
25189
- type: "repository_ids"
25190
- };
25191
- var EXCLUDED_ACTORS_PARAM2 = {
25192
- id: "excluded_actors",
25193
- label: "Exclude users",
25194
- type: "excluded_actors"
25195
- };
25196
- var GROUP_MR_EVENTS_PARAM = {
25197
- id: "group_pr_events",
25198
- label: "Group MR events to same workspace",
25199
- type: "boolean"
25200
- };
25201
- var GITLAB_TRIGGER = {
25202
- type: "gitlab",
25203
- label: "GitLab",
25204
- description: "Trigger on project events",
25205
- icon: "gitlab",
25206
- events: [
25207
- { id: "merge_request.opened", label: "MR opened", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2, GROUP_MR_EVENTS_PARAM] },
25208
- { id: "merge_request.updated", label: "MR updated (new commits)", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2, GROUP_MR_EVENTS_PARAM] },
25209
- { id: "merge_request.merged", label: "MR merged", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2] },
25210
- { id: "merge_request.closed", label: "MR closed", parameters: [REPO_FILTER_PARAM2, EXCLUDED_ACTORS_PARAM2] }
25211
- ]
25212
- };
25213
-
25214
- // ../shared/src/routes/repo-files.ts
25215
- var MAX_REPO_FILE_CONTENT_BYTES = 256 * 1024;
25216
-
25217
- // ../shared/src/routes/admin.ts
25218
- var WEBHOOK_PROVIDERS = ["linear", "github", "gitlab", "slack", "e2b", "stripe", "sentry"];
25219
- var WEBHOOK_PROVIDER_SET = new Set(WEBHOOK_PROVIDERS);
25220
-
25221
- // ../shared/src/display-message/types.ts
25222
- var BACKGROUND_TASK_SUBTYPES = /* @__PURE__ */ new Set([
25223
- "task_started",
25224
- "task_progress",
25225
- "task_updated",
25226
- "task_notification"
25227
- ]);
25228
- function isBackgroundTaskSubtype(subtype) {
25229
- return typeof subtype === "string" && BACKGROUND_TASK_SUBTYPES.has(subtype);
25230
- }
25231
- function optionalString(value) {
25232
- return typeof value === "string" ? value : void 0;
25233
- }
25234
- function coerceBackgroundTaskPayload(payload) {
25235
- if (!isRecord(payload)) return null;
25236
- const subtype = payload.subtype;
25237
- if (!isBackgroundTaskSubtype(subtype)) return null;
25238
- const taskId = payload.task_id;
25239
- if (typeof taskId !== "string" || !taskId) return null;
25240
- const patch = isRecord(payload.patch) ? {
25241
- status: optionalString(payload.patch.status),
25242
- description: optionalString(payload.patch.description),
25243
- error: optionalString(payload.patch.error),
25244
- isBackgrounded: optionalBoolean(payload.patch.is_backgrounded)
25245
- } : void 0;
25246
- const usage = isRecord(payload.usage) ? {
25247
- totalTokens: optionalNumber(payload.usage.total_tokens),
25248
- toolUses: optionalNumber(payload.usage.tool_uses),
25249
- durationMs: optionalNumber(payload.usage.duration_ms)
25250
- } : void 0;
25251
- return {
25252
- subtype,
25253
- taskId,
25254
- description: optionalString(payload.description),
25255
- summary: optionalString(payload.summary),
25256
- outputFile: optionalString(payload.output_file),
25257
- status: optionalString(payload.status),
25258
- taskType: optionalString(payload.task_type),
25259
- workflowName: optionalString(payload.workflow_name),
25260
- prompt: optionalString(payload.prompt),
25261
- lastToolName: optionalString(payload.last_tool_name),
25262
- usage,
25263
- ambient: optionalBoolean(payload.ambient),
25264
- patch
25265
- };
25266
- }
25267
- function optionalNumber(value) {
25268
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
25269
- }
25270
- function optionalBoolean(value) {
25271
- return typeof value === "boolean" ? value : void 0;
25272
- }
25273
- function normalizeBackgroundTaskStatus(status) {
25274
- if (status === "completed" || status === "failed" || status === "stopped") {
25275
- return status;
25276
- }
25277
- if (status === "killed") {
25278
- return "stopped";
25279
- }
25280
- return "in_progress";
25281
- }
25282
-
25283
- // ../shared/src/display-message/constants.ts
25284
- var USER_MESSAGE_MATCH_GRACE_PERIOD_MS = 3e4;
25285
-
25286
- // ../shared/src/user-message-matching.ts
25287
- function parseTimestampMs(timestamp) {
25288
- const value = Date.parse(timestamp);
25289
- return Number.isFinite(value) ? value : 0;
25290
- }
25291
- function areUserMessagesWithinMatchWindow(a, b) {
25292
- return a.content === b.content && Math.abs(parseTimestampMs(a.timestamp) - parseTimestampMs(b.timestamp)) <= USER_MESSAGE_MATCH_GRACE_PERIOD_MS;
25293
- }
25294
-
25295
- // ../shared/src/agent-event-utils.ts
25296
- function getUserMessage(event) {
25297
- return event.type === "event_msg" && event.payload.type === "user_message" && typeof event.payload.message === "string" ? event.payload.message : null;
25298
- }
25299
- function getUserMessageId(event) {
25300
- const messageId = event.payload[USER_MESSAGE_ID_PAYLOAD_KEY];
25301
- return typeof messageId === "string" ? messageId : null;
25302
- }
25303
-
25304
- // ../shared/src/json.ts
25305
- function safeJsonParse(str, fallback) {
25306
- try {
25307
- return JSON.parse(str);
25308
- } catch {
25309
- return fallback;
25310
- }
25311
- }
25312
-
25313
- // ../shared/src/display-message/parsers/utils.ts
25314
- var INTERRUPTED_MESSAGE_REGEX = /^\[Request interrupted by user.*\]$/;
25315
- function userMessageImages(value) {
25316
- if (!Array.isArray(value)) return void 0;
25317
- const images = value.filter((item) => isRecord(item) && item.type === "image" && typeof item.mediaType === "string" && typeof item.data === "string");
25318
- return images.length > 0 ? images : void 0;
25319
- }
25320
- function stringifyDisplayValue(value) {
25321
- if (value === void 0 || value === null) return void 0;
25322
- if (typeof value === "string") return value;
25323
- try {
25324
- return JSON.stringify(value, null, 2);
25325
- } catch {
25326
- return String(value);
25327
- }
25328
- }
25329
- function upsertDisplayMessage(messages, message) {
25330
- const index = messages.findIndex((candidate) => candidate.id === message.id);
25331
- if (index === -1) messages.push(message);
25332
- else messages[index] = message;
25333
- }
25334
- function applyToolInputEvent(event, messages, provider, state, questionFilter) {
25335
- 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))) {
25336
- const resolved = state.resolutions.get(event.payload.toolUseId);
25337
- const inputRequest = {
25338
- requestId: event.payload.requestId,
25339
- status: resolved?.selectionId === "aborted" || resolved?.selectionId === "declined" ? "aborted" : resolved ? "resolved" : "pending",
25340
- ...Array.isArray(event.payload.options) ? { options: event.payload.options } : {},
25341
- ...Array.isArray(event.payload.questions) ? { questions: questionFilter ? event.payload.questions.filter(questionFilter) : event.payload.questions } : {},
25342
- ...resolved ? { selectionId: resolved.selectionId } : {},
25343
- ...resolved?.selectionSummary ? { selectionSummary: resolved.selectionSummary } : {}
25344
- };
25345
- state.requests.set(event.payload.toolUseId, inputRequest);
25346
- const message = messages.find((candidate) => candidate.type === "tool_call" && candidate.id === `${provider}-tool-${event.payload.toolUseId}`);
25347
- if (message) message.inputRequest = inputRequest;
25348
- return true;
25349
- }
25350
- if (event.type === "replicas-tool-input-resolved" && typeof event.payload.toolUseId === "string" && typeof event.payload.selectionId === "string") {
25351
- const resolved = {
25352
- selectionId: event.payload.selectionId,
25353
- ...typeof event.payload.selectionSummary === "string" ? { selectionSummary: event.payload.selectionSummary } : {}
25354
- };
25355
- state.resolutions.set(event.payload.toolUseId, resolved);
25356
- const inputRequest = state.requests.get(event.payload.toolUseId);
25357
- if (inputRequest) {
25358
- inputRequest.status = resolved.selectionId === "aborted" || resolved.selectionId === "declined" ? "aborted" : "resolved";
25359
- inputRequest.selectionId = resolved.selectionId;
25360
- if (resolved.selectionSummary) inputRequest.selectionSummary = resolved.selectionSummary;
25361
- }
25362
- return true;
25363
- }
25364
- return false;
25365
- }
25366
- function parseSkillName(tool, input) {
25367
- if (typeof tool !== "string") return null;
25368
- const normalizedTool = tool.toLowerCase();
25369
- if (normalizedTool.startsWith("skill.")) return tool.slice("skill.".length) || null;
25370
- if (normalizedTool !== "skill") return null;
25371
- const parsedInput = typeof input === "string" ? safeJsonParse(input, null) : input;
25372
- if (!isRecord(parsedInput)) return null;
25373
- const skillName = parsedInput.skill ?? parsedInput.name;
25374
- return typeof skillName === "string" && skillName ? skillName : null;
25375
- }
25376
- function skillNameFromFilePath(path6) {
25377
- const skillName = skillNameFromSkillRootPath(path6);
25378
- if (skillName) return skillName;
25379
- const parts = path6.split(/[\\/]+/).filter(Boolean);
25380
- if (parts.length < 2 || parts[parts.length - 1].toLowerCase() !== "skill.md") return null;
25381
- const fallbackSkillName = parts[parts.length - 2];
25382
- return fallbackSkillName && fallbackSkillName.toLowerCase() !== "skills" ? fallbackSkillName : null;
25383
- }
25384
- function skillNameFromSkillRootPath(path6) {
25385
- const parts = path6.split(/[\\/]+/).filter(Boolean);
25386
- const skillsIndex = parts.findLastIndex((part) => part.toLowerCase() === "skills");
25387
- if (skillsIndex === -1) return null;
25388
- const firstSkillPart = parts[skillsIndex + 1];
25389
- const skillName = firstSkillPart?.toLowerCase() === ".system" ? parts[skillsIndex + 2] : firstSkillPart;
25390
- return skillName && skillName.toLowerCase() !== "skill.md" ? skillName : null;
25391
- }
25392
- function parseSkillFilePath(input) {
25393
- const parsedInput = typeof input === "string" ? safeJsonParse(input, null) : input;
25394
- if (!isRecord(parsedInput)) return null;
25395
- const path6 = [parsedInput.path, parsedInput.filePath, parsedInput.file_path].find((value) => typeof value === "string" && value.length > 0);
25396
- if (!path6) return null;
25397
- return skillNameFromFilePath(path6);
25398
- }
25399
- function skillNameFromToolCall(tool, input) {
25400
- const directSkillName = parseSkillName(tool, input);
25401
- if (directSkillName) return directSkillName;
25402
- if (typeof tool !== "string" || !["read", "read_file", "readfile"].includes(tool.toLowerCase())) return null;
25403
- return parseSkillFilePath(input);
25404
- }
25405
- function createCallDisplayMessage(message) {
25406
- const skillName = message.server.toLowerCase() === "skills" ? message.tool : skillNameFromToolCall(message.tool, message.input);
25407
- if (skillName === null) return { ...message, type: "tool_call" };
25408
- return {
25409
- id: message.id,
25410
- type: "skill",
25411
- skillName,
25412
- status: message.status,
25413
- timestamp: message.timestamp
25414
- };
25415
- }
25416
- function insertDisplayMessageByTimestamp(messages, message) {
25417
- const messageMs = parseTimestampMs(message.timestamp);
25418
- let index = messages.length;
25419
- while (index > 0 && parseTimestampMs(messages[index - 1].timestamp) > messageMs) index--;
25420
- messages.splice(index, 0, message);
25421
- }
25422
-
25423
- // ../shared/src/display-message/format.ts
25424
- function unquoteShellArg(value) {
25425
- const trimmed = value.trim();
25426
- if (trimmed.length < 2) return trimmed;
25427
- if (trimmed[0] === "'" && trimmed[trimmed.length - 1] === "'") {
25428
- return trimmed.slice(1, -1).replace(/'\\''/g, "'");
25429
- }
25430
- if (trimmed[0] === '"' && trimmed[trimmed.length - 1] === '"') {
25431
- return trimmed.slice(1, -1).replace(/\\(["\\$`])/g, "$1");
25432
- }
25433
- return trimmed;
25434
- }
25435
- function shellPayload(command) {
25436
- const match = command.trim().match(/^(?:\S*\/)?(?:ba|z|c|fi)?sh\s+-(?:[a-zA-Z]*c[a-zA-Z]*|c)\s+([\s\S]+)$/);
25437
- return match ? unquoteShellArg(match[1]) : command.trim();
25438
- }
25439
- function splitShellWords(command) {
25440
- const words = [];
25441
- let word = "";
25442
- let quote = null;
25443
- let escaped = false;
25444
- for (const char of command) {
25445
- if (escaped) {
25446
- word += char;
25447
- escaped = false;
25448
- continue;
25449
- }
25450
- if (char === "\\" && quote !== "'") {
25451
- escaped = true;
25452
- continue;
25453
- }
25454
- if (quote) {
25455
- if (char === quote) quote = null;
25456
- else word += char;
25457
- continue;
25458
- }
25459
- if (char === '"' || char === "'") {
25460
- quote = char;
25461
- continue;
25462
- }
25463
- if (/\s/.test(char)) {
25464
- if (word) {
25465
- words.push(word);
25466
- word = "";
25467
- }
25468
- continue;
25469
- }
25470
- word += char;
25471
- }
25472
- if (word) words.push(word);
25473
- return words;
25474
- }
25475
- function commandWords(command) {
25476
- const payload = shellPayload(command);
25477
- if (!payload || /[;|<>]|&&/.test(payload)) return null;
25478
- return splitShellWords(payload);
25479
- }
25480
- function skillNamesFromCommand(command) {
25481
- const words = commandWords(command);
25482
- if (!words) return [];
25483
- return Array.from(new Set(words.flatMap((word) => {
25484
- const skillName = skillNameFromSkillRootPath(word);
25485
- return skillName ? [skillName] : [];
25486
- })));
25487
- }
25488
-
25489
- // ../shared/src/display-message/parsers/codex-parser.ts
25490
- function getStatusFromExitCode(exitCode) {
25491
- return exitCode === 0 ? "completed" : "failed";
25492
- }
25493
- function getPayloadString(event, key) {
25494
- const value = event.payload?.[key];
25495
- return typeof value === "string" ? value : null;
25496
- }
25497
- function displayId(event, eventIndex, prefix) {
25498
- const stableId = getPayloadString(event, USER_MESSAGE_ID_PAYLOAD_KEY) ?? getPayloadString(event, CODEX_ASP_ITEM_ID_PAYLOAD_KEY);
25499
- return stableId ? `${prefix}-${stableId}` : `${prefix}-${event.timestamp}-${eventIndex}`;
25500
- }
25501
- function parseShellOutput(raw) {
25502
- const exitCodeMatch = raw.match(/^Exit code: (\d+)/m) || raw.match(/Process exited with code (\d+)/);
25503
- const exitCode = exitCodeMatch ? parseInt(exitCodeMatch[1], 10) : 0;
25504
- const outputIndex = raw.indexOf("Output:\n");
25505
- const output = outputIndex >= 0 ? raw.slice(outputIndex + "Output:\n".length) : raw;
25506
- 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 };
25507
24735
  }
25508
24736
  function parsePatch(input) {
25509
24737
  const operations = [];
@@ -26180,6 +25408,44 @@ function parseFxEvents(events) {
26180
25408
  return parseAcpEvents(events, "fx");
26181
25409
  }
26182
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
+
26183
25449
  // ../shared/src/display-message/parsers/inference-error.ts
26184
25450
  var TRANSIENT_HTTP_STATUSES = [408, 429, 500, 502, 503, 504, 529];
26185
25451
  var SERVER_ERROR_TEXT_PATTERN = /internal ?server ?error|server ?error|service unavailable|bad gateway|gateway timeout|overloaded|too many requests/i;
@@ -26670,6 +25936,15 @@ function parseMcpToolName(name) {
26670
25936
  return { server, tool };
26671
25937
  }
26672
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
+
26673
25948
  // ../shared/src/display-message/parsers/claude-parser.ts
26674
25949
  var ABORTED_TERMINAL_REASONS = /* @__PURE__ */ new Set(["aborted_streaming", "aborted_tools"]);
26675
25950
  function coerceClaudeResultPayload(payload) {
@@ -27036,577 +26311,1368 @@ function parseClaudeEvents(events, parentToolUseId) {
27036
26311
  timestamp: event.timestamp
27037
26312
  });
27038
26313
  } else {
27039
- messages.push({
27040
- id: `toolcall-${event.timestamp}-${messages.length}`,
27041
- type: "tool_call",
27042
- server: "claude",
27043
- tool: toolName,
27044
- input: toolInput,
27045
- status: "in_progress",
27046
- timestamp: event.timestamp
27047
- });
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;
27048
26686
  }
27049
26687
  }
27050
- });
27051
- }
27052
- if (event.type === "claude-result") {
27053
- const payload = coerceClaudeResultPayload(event.payload);
27054
- if (isClaudeResultError(payload)) {
27055
- const genuineErrors = stripAgentDiagnosticErrors(payload.errors ?? []);
27056
- messages.push({
27057
- id: `error-${event.timestamp}`,
27058
- type: "error",
27059
- message: genuineErrors.length > 0 ? genuineErrors.join("\n") : "Claude session encountered an unexpected error.",
27060
- timestamp: event.timestamp
27061
- });
27062
- }
27063
- }
27064
- if (event.type === "claude-system") {
27065
- const payload = coerceBackgroundTaskPayload(event.payload);
27066
- if (payload?.ambient) ambientTaskIds.add(payload.taskId);
27067
- if (payload && !ambientTaskIds.has(payload.taskId)) {
27068
- const existing = taskMessageMap.get(payload.taskId);
27069
- const nextStatus = payload.subtype === "task_notification" ? normalizeBackgroundTaskStatus(payload.status) : payload.subtype === "task_updated" ? normalizeBackgroundTaskStatus(payload.patch?.status) : existing?.status ?? "in_progress";
27070
- const nextMessage = {
27071
- id: existing?.id ?? `task-${event.timestamp}-${messages.length}`,
27072
- type: "background_task",
27073
- taskId: payload.taskId,
27074
- status: nextStatus,
27075
- description: payload.description ?? payload.patch?.description ?? existing?.description,
27076
- summary: payload.summary ?? existing?.summary,
27077
- outputFile: payload.outputFile ?? existing?.outputFile,
27078
- taskType: payload.taskType ?? existing?.taskType,
27079
- workflowName: payload.workflowName ?? existing?.workflowName,
27080
- prompt: payload.prompt ?? existing?.prompt,
27081
- lastToolName: payload.lastToolName ?? existing?.lastToolName,
27082
- usage: payload.usage ?? existing?.usage,
27083
- error: payload.patch?.error ?? existing?.error,
27084
- isBackgrounded: payload.patch?.isBackgrounded ?? existing?.isBackgrounded,
27085
- timestamp: existing?.timestamp ?? event.timestamp
27086
- };
27087
- if (existing) {
27088
- Object.assign(existing, nextMessage);
27089
- } else {
27090
- taskMessageMap.set(payload.taskId, nextMessage);
27091
- messages.push(nextMessage);
27092
- }
27093
26688
  }
27094
26689
  }
27095
- });
27096
- filteredEvents.forEach((event) => {
27097
- if (event.type === "replicas-tool-input-request") {
27098
- const payload = event.payload;
27099
- if (!payload.toolUseId || !payload.requestId) return;
27100
- if (!Array.isArray(payload.options) && !Array.isArray(payload.questions)) return;
27101
- const toolInfo = toolCallMap.get(payload.toolUseId);
27102
- if (!toolInfo) return;
27103
- const message = messages[toolInfo.messageIndex];
27104
- if (!message || message.type !== "tool_call") return;
27105
- message.inputRequest = {
27106
- requestId: payload.requestId,
27107
- status: "pending",
27108
- options: payload.options,
27109
- questions: payload.questions
27110
- };
27111
- }
27112
- if (event.type === "replicas-tool-input-resolved") {
27113
- const payload = event.payload;
27114
- if (!payload.toolUseId || !payload.selectionId) return;
27115
- const toolInfo = toolCallMap.get(payload.toolUseId);
27116
- if (!toolInfo) return;
27117
- const message = messages[toolInfo.messageIndex];
27118
- if (!message || message.type !== "tool_call" || !message.inputRequest) return;
27119
- message.inputRequest = {
27120
- ...message.inputRequest,
27121
- status: payload.selectionId === "aborted" ? "aborted" : "resolved",
27122
- selectionId: payload.selectionId,
27123
- 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
27124
26697
  };
26698
+ firstIndexById.set(message.id, duplicateIndex);
26699
+ indexMessage(merged[duplicateIndex], duplicateIndex);
27125
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;
27126
26715
  });
27127
- filteredEvents.forEach((event) => {
27128
- if (event.type === "claude-user") {
27129
- const content = normalizeContentBlocks(event.payload.message?.content);
27130
- const toolResult = content.find((c) => c.type === "tool_result");
27131
- if (toolResult && toolResult.tool_use_id) {
27132
- const toolInfo = toolCallMap.get(toolResult.tool_use_id);
27133
- if (!toolInfo) return;
27134
- const resultContent = extractToolResultText(toolResult.content);
27135
- const isError = toolResult.is_error || false;
27136
- const status = isError ? "failed" : "completed";
27137
- if (toolInfo.toolName === "TaskCreate" && !isError) {
27138
- taskAccumulator.processContentBlock(toolResult);
27139
- }
27140
- const message = messages[toolInfo.messageIndex];
27141
- if (!message) return;
27142
- if (message.type === "command") {
27143
- message.output = resultContent;
27144
- message.status = status;
27145
- const exitCodeMatch = resultContent.match(/exit code:?\s*(\d+)/i);
27146
- if (exitCodeMatch) {
27147
- message.exitCode = parseInt(exitCodeMatch[1], 10);
27148
- } else {
27149
- message.exitCode = isError ? 1 : 0;
27150
- }
27151
- } else if (message.type === "patch") {
27152
- message.status = status;
27153
- } else if (message.type === "web_search") {
27154
- message.status = status;
27155
- } else if (message.type === "tool_call") {
27156
- message.output = resultContent;
27157
- message.status = status;
27158
- } else if (message.type === "skill") {
27159
- message.status = status;
27160
- } else if (message.type === "subagent") {
27161
- message.output = resultContent;
27162
- message.status = status;
27163
- if (!message.chatId && resultContent) {
27164
- const parsed = safeJsonParse(resultContent, {});
27165
- if (parsed.chatId) {
27166
- message.chatId = parsed.chatId;
27167
- }
27168
- }
27169
- } else if (message.type === "subagent_followup") {
27170
- message.output = resultContent;
27171
- message.status = status;
27172
- }
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
27173
26739
  }
27174
- }
26740
+ }];
27175
26741
  });
27176
- for (const message of messages) {
27177
- if (message.type !== "subagent_followup") continue;
27178
- const target = messages.find((candidate) => candidate.type === "subagent" && candidate.chatId === message.chatId);
27179
- if (target?.type === "subagent") {
27180
- message.targetTitle = target.description;
27181
- message.targetProvider = target.subagentType;
27182
- }
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);
27183
26753
  }
27184
- const staleIndexes = new Set(supersededUserMessageIndexes);
27185
- for (const streamId of [...completedStreamIds, ...supersededStreamIds]) {
27186
- const refs = partialIndexes.get(streamId);
27187
- if (refs?.reasoning !== void 0) staleIndexes.add(refs.reasoning);
27188
- 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));
27189
26760
  }
27190
- if (staleIndexes.size === 0) return messages;
27191
- return messages.filter((_2, index) => !staleIndexes.has(index));
26761
+ return messages;
27192
26762
  }
27193
26763
 
27194
- // ../shared/src/display-message/parsers/codex-asp-parser.ts
27195
- var DUPLICATE_WINDOW_MS = 5 * 60 * 1e3;
27196
- function nearTimestamp(a, b) {
27197
- return Math.abs(parseTimestampMs(a) - parseTimestampMs(b)) <= DUPLICATE_WINDOW_MS;
27198
- }
27199
- function inputForDisplay(input) {
27200
- if (input === void 0 || input === null) return void 0;
27201
- if (typeof input === "string") return input;
27202
- if (isRecord(input)) return input;
27203
- return String(input);
27204
- }
27205
- function isInternalUserMessage(content) {
27206
- const trimmed = content.trim();
27207
- return trimmed.startsWith("<environment_context>") || trimmed.startsWith("<goal_context>");
27208
- }
27209
- function messageForItem(item) {
27210
- if (item.type === "userMessage") {
27211
- if (isInternalUserMessage(item.content)) return null;
27212
- return {
27213
- id: `user-${item.id}`,
27214
- type: "user",
27215
- content: item.content,
27216
- images: item.images,
27217
- timestamp: item.timestamp
27218
- };
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);
27219
26769
  }
27220
- if (item.type === "agentMessage") {
27221
- if (!item.text) return null;
27222
- return {
27223
- id: `agent-${item.id}`,
27224
- type: "agent",
27225
- content: item.text,
27226
- timestamp: item.timestamp
27227
- };
26770
+ if (agentType === "cursor") {
26771
+ return parseCursorEvents(events);
27228
26772
  }
27229
- if (item.type === "reasoning") {
27230
- if (!item.text) return null;
27231
- return {
27232
- id: `reasoning-${item.id}`,
27233
- type: "reasoning",
27234
- content: item.text,
27235
- status: normalizeCodexAspTranscriptStatus(item.status),
27236
- timestamp: item.timestamp,
27237
- ...item.sourceTimestamp ? { sourceTimestamp: item.sourceTimestamp } : {}
27238
- };
26773
+ if (agentType === "deepseek") {
26774
+ return parseDeepseekEvents(events);
27239
26775
  }
27240
- if (item.type === "commandExecution") {
27241
- const skillNames = skillNamesFromCommand(item.command);
27242
- return {
27243
- id: `command-${item.id}`,
27244
- type: "command",
27245
- command: item.command,
27246
- ...skillNames.length > 0 ? { skillNames } : {},
27247
- output: item.output,
27248
- exitCode: item.exitCode ?? void 0,
27249
- status: normalizeCodexAspTranscriptStatus(item.status),
27250
- timestamp: item.timestamp
27251
- };
26776
+ if (agentType === "fx") {
26777
+ return parseFxEvents(events);
27252
26778
  }
27253
- if (item.type === "fileChange") {
27254
- return {
27255
- id: `patch-${item.id}`,
27256
- type: "patch",
27257
- operations: item.operations,
27258
- output: item.output,
27259
- exitCode: item.exitCode ?? void 0,
27260
- status: normalizeCodexAspTranscriptStatus(item.status),
27261
- timestamp: item.timestamp
27262
- };
26779
+ if (agentType === "kimi") {
26780
+ return parseAcpEvents(events, "kimi");
27263
26781
  }
27264
- if (item.type === "toolCall") {
27265
- return createCallDisplayMessage({
27266
- id: `toolcall-${item.id}`,
27267
- server: item.server,
27268
- tool: item.tool,
27269
- input: inputForDisplay(item.input),
27270
- output: item.output,
27271
- status: normalizeCodexAspTranscriptStatus(item.status),
27272
- timestamp: item.timestamp
27273
- });
26782
+ if (agentType === "muse") {
26783
+ return parseMuseEvents(events);
27274
26784
  }
27275
- if (item.type === "subagent") {
27276
- return {
27277
- id: `subagent-${item.id}`,
27278
- type: "subagent",
27279
- toolUseId: item.id,
27280
- description: item.description,
27281
- prompt: item.prompt,
27282
- subagentType: item.subagentType,
27283
- ...item.receiverThreadIds ? { receiverThreadIds: item.receiverThreadIds } : {},
27284
- ...item.model ? { model: item.model } : {},
27285
- status: normalizeCodexAspTranscriptStatus(item.status),
27286
- ...item.output ? { output: item.output } : {},
27287
- nestedEvents: [],
27288
- timestamp: item.timestamp
27289
- };
26785
+ if (agentType === "opencode") {
26786
+ return parseOpencodeEvents(events);
27290
26787
  }
27291
- if (item.type === "webSearch") {
27292
- return {
27293
- id: `web-search-${item.id}`,
27294
- type: "web_search",
27295
- query: item.query,
27296
- status: normalizeCodexAspTranscriptStatus(item.status),
27297
- timestamp: item.timestamp
27298
- };
26788
+ if (agentType === "pi") {
26789
+ return parsePiEvents(events);
27299
26790
  }
27300
- if (item.type === "plan") {
27301
- if (!item.text) return null;
27302
- return {
27303
- id: `todo-${item.id}`,
27304
- type: "todo_list",
27305
- items: item.text.split("\n").map((line) => line.trim()).filter(Boolean).map((text) => ({ text, completed: item.status === "completed" })),
27306
- status: normalizeCodexAspTranscriptStatus(item.status),
27307
- timestamp: item.timestamp
27308
- };
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);
27309
26800
  }
27310
- if (item.type === "contextCompaction") {
27311
- return {
27312
- id: `reasoning-${item.id}`,
27313
- type: "reasoning",
27314
- content: "Context compacted",
27315
- status: normalizeCodexAspTranscriptStatus(item.status),
27316
- 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 } : {}
27317
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
+ ]
27318
26964
  }
27319
- return {
27320
- id: `error-${item.id}`,
27321
- type: "error",
27322
- message: item.message,
27323
- timestamp: item.timestamp
27324
- };
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;
27325
26972
  }
27326
- function skillMessagesForItem(item) {
27327
- if (item.type !== "userMessage" || !item.skillNames) return [];
27328
- return item.skillNames.filter((skillName) => skillName.trim().length > 0).map((skillName, index) => ({
27329
- id: `skill-${item.id}-${index}`,
27330
- type: "skill",
27331
- skillName,
27332
- status: "completed",
27333
- timestamp: item.timestamp
27334
- }));
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;
27335
26978
  }
27336
- function stableString(value) {
27337
- if (value === void 0) return "";
27338
- if (typeof value !== "object" || value === null) return String(value);
27339
- try {
27340
- return JSON.stringify(value, Object.keys(value).sort());
27341
- } catch {
27342
- 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;
27343
27154
  }
27155
+ return 0;
27344
27156
  }
27345
- function duplicateKey(message) {
27346
- if (message.type === "user" || message.type === "agent" || message.type === "reasoning") {
27347
- return JSON.stringify([message.type, message.content]);
27348
- }
27349
- if (message.type === "command") return JSON.stringify([message.type, message.command]);
27350
- if (message.type === "web_search") return JSON.stringify([message.type, message.query]);
27351
- if (message.type === "subagent") {
27352
- return JSON.stringify([
27353
- message.type,
27354
- message.description,
27355
- message.prompt,
27356
- message.subagentType,
27357
- message.model
27358
- ]);
27359
- }
27360
- if (message.type === "todo_list") {
27361
- return JSON.stringify([message.type, stableString(message.items)]);
27362
- }
27363
- if (message.type === "patch") {
27364
- return JSON.stringify([message.type, stableString(message.operations)]);
27365
- }
27366
- if (message.type === "tool_call") {
27367
- return JSON.stringify([
27368
- message.type,
27369
- message.server,
27370
- message.tool,
27371
- stableString(message.input)
27372
- ]);
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"]
27373
27340
  }
27374
- return null;
27375
- }
27376
- function duplicateBucketKey(message, bucketOffset = 0) {
27377
- const key = duplicateKey(message);
27378
- if (key === null) return null;
27379
- const bucket = Math.floor(parseTimestampMs(message.timestamp) / DUPLICATE_WINDOW_MS) + bucketOffset;
27380
- 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";
27381
27456
  }
27382
- function mergeCodexAspDisplayMessages(primary, supplemental) {
27383
- const merged = [...primary];
27384
- const primaryCount = primary.length;
27385
- const firstIndexById = /* @__PURE__ */ new Map();
27386
- const indexesByDuplicateBucket = /* @__PURE__ */ new Map();
27387
- const indexMessage = (message, index) => {
27388
- if (!firstIndexById.has(message.id)) firstIndexById.set(message.id, index);
27389
- const key = duplicateBucketKey(message);
27390
- if (key === null) return;
27391
- const indexes = indexesByDuplicateBucket.get(key) ?? /* @__PURE__ */ new Set();
27392
- indexes.add(index);
27393
- indexesByDuplicateBucket.set(key, indexes);
27394
- };
27395
- const unindexMessage = (message, index) => {
27396
- const key = duplicateBucketKey(message);
27397
- if (key === null) return;
27398
- const indexes = indexesByDuplicateBucket.get(key);
27399
- indexes?.delete(index);
27400
- if (indexes?.size === 0) indexesByDuplicateBucket.delete(key);
27457
+ function workspaceConfigWithCapabilities(config3, capabilities = {}) {
27458
+ const mergedCapabilities = {
27459
+ ...config3?.capabilities,
27460
+ ...capabilities
27401
27461
  };
27402
- merged.forEach(indexMessage);
27403
- for (const message of supplemental) {
27404
- let duplicateIndex = firstIndexById.get(message.id);
27405
- if (duplicateIndex === void 0) {
27406
- for (let bucketOffset = -1; bucketOffset <= 1; bucketOffset += 1) {
27407
- const key = duplicateBucketKey(message, bucketOffset);
27408
- for (const index of (key === null ? void 0 : indexesByDuplicateBucket.get(key)) ?? []) {
27409
- if (nearTimestamp(merged[index].timestamp, message.timestamp) && (duplicateIndex === void 0 || index < duplicateIndex)) {
27410
- duplicateIndex = index;
27411
- }
27412
- }
27413
- }
27414
- }
27415
- if (duplicateIndex === void 0) {
27416
- indexMessage(message, merged.push(message) - 1);
27417
- } else if (message.type === "user") {
27418
- unindexMessage(merged[duplicateIndex], duplicateIndex);
27419
- merged[duplicateIndex] = {
27420
- ...merged[duplicateIndex],
27421
- id: message.id
27422
- };
27423
- firstIndexById.set(message.id, duplicateIndex);
27424
- indexMessage(merged[duplicateIndex], duplicateIndex);
27462
+ return {
27463
+ ...config3,
27464
+ capabilities: {
27465
+ ...mergedCapabilities,
27466
+ pr_followups: mergedCapabilities.pr_followups === true
27425
27467
  }
27426
- }
27427
- const ordered = merged.slice(0, primaryCount);
27428
- 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);
27429
- for (const { message } of supplementalOnly) {
27430
- insertDisplayMessageByTimestamp(ordered, message);
27431
- }
27432
- return ordered;
27468
+ };
27433
27469
  }
27434
- function parseCodexAspTranscript(transcript) {
27435
- const messages = [];
27436
- const orderedItems = transcript.turns.map((turn, turnIndex) => ({ turn, turnIndex })).flatMap(({ turn, turnIndex }) => turn.items.map((item, itemIndex) => ({ item, turn, turnIndex, itemIndex }))).sort((a, b) => {
27437
- const aSequence = a.item.sequence ?? Number.MAX_SAFE_INTEGER;
27438
- const bSequence = b.item.sequence ?? Number.MAX_SAFE_INTEGER;
27439
- return aSequence - bSequence || parseTimestampMs(a.item.timestamp) - parseTimestampMs(b.item.timestamp) || a.turnIndex - b.turnIndex || a.itemIndex - b.itemIndex;
27440
- });
27441
- const reasoningByTurnId = /* @__PURE__ */ new Map();
27442
- for (const { item, turn } of orderedItems) {
27443
- if (item.type !== "reasoning" || !item.text.trim()) continue;
27444
- const reasoning = reasoningByTurnId.get(turn.id) ?? [];
27445
- reasoning.push(item);
27446
- reasoningByTurnId.set(turn.id, reasoning);
27447
- }
27448
- const emittedReasoningTurnIds = /* @__PURE__ */ new Set();
27449
- const coalescedItems = orderedItems.flatMap((entry) => {
27450
- if (entry.item.type !== "reasoning") return [entry];
27451
- const reasoning = reasoningByTurnId.get(entry.turn.id);
27452
- if (!reasoning || emittedReasoningTurnIds.has(entry.turn.id)) return [];
27453
- emittedReasoningTurnIds.add(entry.turn.id);
27454
- return [{
27455
- ...entry,
27456
- item: {
27457
- type: "reasoning",
27458
- id: entry.turn.id,
27459
- text: reasoning.map(({ text }) => text.trim()).join("\n\n"),
27460
- timestamp: entry.turn.startedAt,
27461
- ...reasoning[0] ? { sourceTimestamp: reasoning[0].timestamp } : {},
27462
- status: reasoning.at(-1)?.status ?? "completed",
27463
- sequence: entry.item.sequence
27464
- }
27465
- }];
27466
- });
27467
- for (const turn of transcript.turns) {
27468
- const reasoningIndex = coalescedItems.findIndex(({ item, turn: itemTurn }) => itemTurn.id === turn.id && item.type === "reasoning");
27469
- if (reasoningIndex === -1) continue;
27470
- const responseItem = getCodexAspTurnResponse({
27471
- status: turn.status,
27472
- items: coalescedItems.filter(({ turn: itemTurn }) => itemTurn.id === turn.id).map(({ item }) => item)
27473
- });
27474
- const finalAnswerIndex = responseItem ? coalescedItems.findIndex(({ item }) => item === responseItem) : -1;
27475
- if (finalAnswerIndex === -1 || reasoningIndex < finalAnswerIndex) continue;
27476
- const [reasoning] = coalescedItems.splice(reasoningIndex, 1);
27477
- coalescedItems.splice(finalAnswerIndex, 0, reasoning);
27478
- }
27479
- for (const { item } of coalescedItems) {
27480
- const message = messageForItem(item);
27481
- if (message) {
27482
- messages.push(message);
27483
- }
27484
- messages.push(...skillMessagesForItem(item));
27485
- }
27486
- 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";
27487
27559
  }
27488
27560
 
27489
- // ../shared/src/display-message/parsers/index.ts
27490
- var INTERRUPTION_DEDUP_WINDOW_MS = 15e3;
27491
- function parseAgentEvents(events, agentType) {
27492
- if (agentType === "codex") {
27493
- return parseCodexEvents(events);
27494
- }
27495
- if (agentType === "cursor") {
27496
- return parseCursorEvents(events);
27497
- }
27498
- if (agentType === "deepseek") {
27499
- return parseDeepseekEvents(events);
27500
- }
27501
- if (agentType === "fx") {
27502
- return parseFxEvents(events);
27503
- }
27504
- if (agentType === "kimi") {
27505
- return parseAcpEvents(events, "kimi");
27506
- }
27507
- if (agentType === "muse") {
27508
- return parseMuseEvents(events);
27509
- }
27510
- if (agentType === "opencode") {
27511
- return parseOpencodeEvents(events);
27512
- }
27513
- if (agentType === "pi") {
27514
- return parsePiEvents(events);
27515
- }
27516
- 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);
27517
27565
  }
27518
- function parseDisplayMessages(events, agentType, codexAspTranscript, options = {}) {
27519
- const shouldFilter = options.filter ?? true;
27520
- const parsedEvents = agentType === "claude" || agentType === "relay" ? parseClaudeEvents(events, options.parentToolUseId) : parseAgentEvents(events, agentType);
27521
- const reconciledEvents = [];
27522
- for (const message of parsedEvents) {
27523
- if (message.type === "user") upsertDisplayMessage(reconciledEvents, message);
27524
- else reconciledEvents.push(message);
27525
- }
27526
- const legacyMessages = shouldFilter ? filterDisplayMessages(reconciledEvents, agentType) : reconciledEvents;
27527
- const applySyntheticNotices = (messages) => shouldFilter ? applyAuthFallbackNotices(applyInterruptions(messages, events), events) : messages;
27528
- if (agentType !== "codex" || !codexAspTranscript) {
27529
- return applySyntheticNotices(legacyMessages);
27530
- }
27531
- const nativeCodexMessages = shouldFilter ? filterDisplayMessages(parseCodexAspTranscript(codexAspTranscript), agentType) : parseCodexAspTranscript(codexAspTranscript);
27532
- return applySyntheticNotices(mergeCodexAspDisplayMessages(nativeCodexMessages, legacyMessages));
27566
+ function normalizeAutomationGithubCheckNames(names) {
27567
+ return names.map((name) => name.trim()).filter(Boolean);
27533
27568
  }
27534
- function applyInterruptions(messages, events) {
27535
- const result = [...messages];
27536
- for (const event of events) {
27537
- if (event.type !== CHAT_INTERRUPTED_EVENT_TYPE) continue;
27538
- insertDisplayMessageByTimestamp(result, {
27539
- id: `interruption-${event.timestamp}`,
27540
- type: "interruption",
27541
- timestamp: event.timestamp
27542
- });
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
+ }
27543
27577
  }
27544
- let lastUserMs = null;
27545
- let lastInterruption = null;
27546
- const finalized = [];
27547
- for (const msg of result) {
27548
- if (msg.type === "user") {
27549
- lastUserMs = parseTimestampMs(msg.timestamp);
27550
- lastInterruption = null;
27551
- finalized.push(msg);
27552
- 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" });
27553
27587
  }
27554
- if (msg.type !== "interruption") {
27555
- finalized.push(msg);
27556
- 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)" });
27557
27591
  }
27558
- const ms = parseTimestampMs(msg.timestamp);
27559
- if (lastInterruption && ms - parseTimestampMs(lastInterruption.timestamp) <= INTERRUPTION_DEDUP_WINDOW_MS) {
27560
- 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(", ")}` });
27561
27595
  }
27562
- lastInterruption = {
27563
- ...msg,
27564
- ...lastUserMs !== null && ms >= lastUserMs ? { durationMs: ms - lastUserMs } : {}
27565
- };
27566
- finalized.push(lastInterruption);
27567
27596
  }
27568
- return finalized;
27569
- }
27570
- function applyAuthFallbackNotices(messages, events) {
27571
- const notices = [];
27572
- events.forEach((event, index) => {
27573
- if (event.type !== AUTH_FALLBACK_EVENT_TYPE) return;
27574
- const payload = coerceAuthFallbackPayload(event.payload);
27575
- if (!payload) return;
27576
- notices.push({
27577
- ...payload,
27578
- id: `auth-fallback-${event.timestamp}-${index}`,
27579
- type: "auth_fallback",
27580
- timestamp: event.timestamp
27581
- });
27582
- });
27583
- if (notices.length === 0) return messages;
27584
- const result = [...messages];
27585
- for (const notice of notices) insertDisplayMessageByTimestamp(result, notice);
27586
- return result;
27587
- }
27588
- function isCodexInitializationPrompt(message) {
27589
- return message.type === "user" && removeReplicasInstructions(message.content).trim() === "Hello";
27590
- }
27591
- function filterDisplayMessages(messages, provider) {
27592
- let result = messages;
27593
- if (provider === "codex") {
27594
- const userMessages = result.map((msg, index) => ({ msg, index })).filter(({ msg }) => msg.type === "user");
27595
- if (userMessages.length >= 2 && isCodexInitializationPrompt(userMessages[0].msg)) {
27596
- 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` });
27597
27601
  }
27598
- }
27599
- result = result.map((msg) => {
27600
- if (msg.type !== "user") return msg;
27601
- const cleaned = removeReplicasInstructions(msg.content).trim();
27602
- if (INTERRUPTED_MESSAGE_REGEX.test(cleaned)) {
27603
- 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(", ")}` });
27604
27605
  }
27605
- return cleaned !== msg.content ? { ...msg, content: cleaned } : msg;
27606
- });
27607
- return result;
27606
+ }
27607
+ return createSuccessResult({ agentProvider, model, thinkingLevel });
27608
27608
  }
27609
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
+
27610
27676
  // ../shared/src/onboarding-emails.ts
27611
27677
  var EMAIL_KEYS = [
27612
27678
  "welcome",