replicas-engine 0.1.625 → 0.1.627

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/src/index.js CHANGED
@@ -2101,6 +2101,7 @@ Use this when:
2101
2101
  - The user asks for ClickHouse analytics or PostHog product data
2102
2102
  - The user asks to invoke a connected Modal function
2103
2103
  - The user asks to query or update a connected turbopuffer namespace
2104
+ - The user asks for PlanetScale organizations, databases, branches, or Insights data
2104
2105
  - The user asks to analyze text with Pangram
2105
2106
  - The user asks for Stripe customers, payments, invoices, subscriptions, or balances`;
2106
2107
  var REFERENCE9 = `# Plugins
@@ -2156,6 +2157,17 @@ const results = await Promise.all(customerIds.map((customer) =>
2156
2157
 
2157
2158
  Never attempt to import Composio, create a session, manage a connection, or call a provider with raw credentials. Those operations are intentionally unavailable inside the workspace.
2158
2159
 
2160
+ ## PlanetScale
2161
+
2162
+ PlanetScale is a native OAuth integration. Pass a relative path from the PlanetScale v1 API; Replicas refreshes access tokens server-side:
2163
+
2164
+ \`\`\`ts
2165
+ const organizations = await replicas.planetscale.request('/organizations');
2166
+ const databases = await replicas.planetscale.request('/organizations/acme/databases');
2167
+ \`\`\`
2168
+
2169
+ OAuth scopes control permitted operations. Confirm mutations before using POST, PUT, PATCH, or DELETE.
2170
+
2159
2171
  ## Modal
2160
2172
 
2161
2173
  Modal is a native integration and uses its official JavaScript SDK behind the Replicas gateway. Invoke a deployed function by name:
@@ -5363,6 +5375,63 @@ var MEDIA_KIND = {
5363
5375
  var MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO, MEDIA_KIND.HTML];
5364
5376
  var PUBLIC_MEDIA_KINDS = [MEDIA_KIND.IMAGE, MEDIA_KIND.VIDEO, MEDIA_KIND.AUDIO];
5365
5377
  var MAX_ENGINE_LOG_BYTES = 50 * 1024 * 1024;
5378
+ function normalizeChatMessageSenders(value) {
5379
+ if (!Array.isArray(value)) return void 0;
5380
+ const senders = value.flatMap((sender) => {
5381
+ if (!isChatMessageSender(sender)) return [];
5382
+ const { senderUserId, senderEmail, senderDisplayName, senderAvatarUrl, recordedAt } = sender;
5383
+ return [{
5384
+ senderUserId,
5385
+ senderEmail,
5386
+ ...typeof senderDisplayName === "string" ? { senderDisplayName } : {},
5387
+ ...typeof senderAvatarUrl === "string" ? { senderAvatarUrl } : {},
5388
+ recordedAt
5389
+ }];
5390
+ });
5391
+ return senders;
5392
+ }
5393
+ function normalizeChatTranscriptMetadata(value) {
5394
+ if (!isRecord(value)) return {};
5395
+ const metadata = {};
5396
+ const {
5397
+ provider,
5398
+ title,
5399
+ createdAt,
5400
+ updatedAt,
5401
+ parentChatId,
5402
+ deletedAt,
5403
+ senders,
5404
+ captureId,
5405
+ captureReason,
5406
+ sha256
5407
+ } = value;
5408
+ if (typeof provider === "string" && isValidAgentProvider(provider)) metadata.provider = provider;
5409
+ if (typeof title === "string") metadata.title = title;
5410
+ if (typeof createdAt === "string") metadata.createdAt = createdAt;
5411
+ if (typeof updatedAt === "string") metadata.updatedAt = updatedAt;
5412
+ if (typeof parentChatId === "string" || parentChatId === null) metadata.parentChatId = parentChatId;
5413
+ if (typeof deletedAt === "string" || deletedAt === null) metadata.deletedAt = deletedAt;
5414
+ const normalizedSenders = normalizeChatMessageSenders(senders);
5415
+ if (normalizedSenders) metadata.senders = normalizedSenders;
5416
+ if (typeof captureId === "string") metadata.captureId = captureId;
5417
+ if (captureReason === "workspace_sleep") metadata.captureReason = captureReason;
5418
+ if (typeof sha256 === "string") metadata.sha256 = sha256;
5419
+ return metadata;
5420
+ }
5421
+ function parseChatTranscriptArtifact(value) {
5422
+ if (!isRecord(value) || typeof value.id !== "string" || typeof value.workspace_id !== "string" || value.kind !== "chat_transcript" || value.provider !== "s3" || typeof value.bucket !== "string" || typeof value.key !== "string" || typeof value.size !== "number" || typeof value.created_at !== "string") return null;
5423
+ return {
5424
+ id: value.id,
5425
+ workspace_id: value.workspace_id,
5426
+ kind: "chat_transcript",
5427
+ provider: "s3",
5428
+ bucket: value.bucket,
5429
+ key: value.key,
5430
+ size: value.size,
5431
+ metadata: normalizeChatTranscriptMetadata(value.metadata),
5432
+ created_at: value.created_at
5433
+ };
5434
+ }
5366
5435
 
5367
5436
  // ../shared/src/memory.ts
5368
5437
  var MEMORY_ROOT = `${SANDBOX_PATHS.REPLICAS_DIR}/memories`;
@@ -10960,7 +11029,7 @@ var DEFAULT_CODEX_ARGS = [
10960
11029
  var MIN_CODEX_CLI_VERSION = "0.144.6";
10961
11030
  var CODEX_UPGRADE_TIMEOUT_MS = 12e4;
10962
11031
  var codexCliVersionEnsured = null;
10963
- var ENGINE_PACKAGE_VERSION = "0.1.625";
11032
+ var ENGINE_PACKAGE_VERSION = "0.1.627";
10964
11033
  var INITIALIZE_METHOD = "initialize";
10965
11034
  var INITIALIZED_NOTIFICATION = "initialized";
10966
11035
  var ACCOUNT_LOGIN_START_METHOD = "account/login/start";
@@ -15920,6 +15989,7 @@ async function reconcileCanvasItems(filenames) {
15920
15989
 
15921
15990
  // src/services/upload-chat-transcripts.ts
15922
15991
  import { createReadStream } from "fs";
15992
+ import { createHash as createHash2 } from "crypto";
15923
15993
  import { readdir as readdir8, readFile as readFile14, stat as stat4 } from "fs/promises";
15924
15994
  import { request as httpRequest } from "http";
15925
15995
  import { request as httpsRequest } from "https";
@@ -15977,9 +16047,10 @@ async function putTranscript(uploadUrl, filePath, size) {
15977
16047
  file.pipe(request);
15978
16048
  });
15979
16049
  }
15980
- async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
16050
+ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map(), capture) {
15981
16051
  let flushed = 0;
15982
16052
  let failed = 0;
16053
+ const revisions = [];
15983
16054
  const tasks = [];
15984
16055
  for (const dir of HISTORY_DIRS) {
15985
16056
  let entries;
@@ -15992,8 +16063,9 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
15992
16063
  if (!entry.endsWith(".jsonl")) continue;
15993
16064
  const chatId = basename2(entry, ".jsonl");
15994
16065
  tasks.push(
15995
- uploadChatTranscript(chatId, join25(dir, entry), chatsById.get(chatId)).then(() => {
16066
+ uploadChatTranscript(chatId, join25(dir, entry), chatsById.get(chatId), capture).then((artifact) => {
15996
16067
  flushed++;
16068
+ if (artifact && capture) revisions.push(artifact);
15997
16069
  }).catch((err) => {
15998
16070
  failed++;
15999
16071
  console.error("[ChatTranscriptUploader] upload failed:", { chatId, err });
@@ -16002,11 +16074,11 @@ async function flushAllChatTranscripts(chatsById = /* @__PURE__ */ new Map()) {
16002
16074
  }
16003
16075
  }
16004
16076
  await Promise.all(tasks);
16005
- return { flushed, failed };
16077
+ return capture ? { flushed, failed, captureSupported: true, revisions } : { flushed, failed };
16006
16078
  }
16007
- async function uploadChatTranscript(chatId, filePath, chat) {
16079
+ async function uploadChatTranscript(chatId, filePath, chat, capture) {
16008
16080
  const { size } = await stat4(filePath);
16009
- if (size === 0) return;
16081
+ if (size === 0) return null;
16010
16082
  const metadata = chat ? {
16011
16083
  provider: chat.provider,
16012
16084
  title: chat.title,
@@ -16015,6 +16087,13 @@ async function uploadChatTranscript(chatId, filePath, chat) {
16015
16087
  parentChatId: chat.parentChatId,
16016
16088
  deletedAt: chat.deletedAt ?? null
16017
16089
  } : {};
16090
+ if (capture) {
16091
+ const hash = createHash2("sha256");
16092
+ for await (const chunk of createReadStream(filePath)) hash.update(chunk);
16093
+ metadata.captureId = capture.captureId;
16094
+ metadata.captureReason = capture.reason;
16095
+ metadata.sha256 = hash.digest("hex");
16096
+ }
16018
16097
  try {
16019
16098
  metadata.senders = parseChatMessageSendersJsonl(
16020
16099
  await readFile14(chatMessageSendersFilePath(chatId), "utf-8")
@@ -16033,7 +16112,7 @@ async function uploadChatTranscript(chatId, filePath, chat) {
16033
16112
  if (!isRecord(prepareBody) || typeof prepareBody.uploadUrl !== "string" && prepareBody.uploadUrl !== null) {
16034
16113
  throw new Error("prepare failed: invalid response");
16035
16114
  }
16036
- if (prepareBody.uploadUrl === null) return;
16115
+ if (prepareBody.uploadUrl === null) return null;
16037
16116
  await putTranscript(prepareBody.uploadUrl, filePath, size);
16038
16117
  const finalizeResponse = await monolithRequest("/v1/engine/chat-transcripts/finalize", {
16039
16118
  body: uploadRequest
@@ -16041,6 +16120,12 @@ async function uploadChatTranscript(chatId, filePath, chat) {
16041
16120
  if (!finalizeResponse.ok) {
16042
16121
  throw new Error(`finalize failed: ${finalizeResponse.status} ${await finalizeResponse.text()}`);
16043
16122
  }
16123
+ const finalizeBody = await finalizeResponse.json();
16124
+ if (!isRecord(finalizeBody)) throw new Error("finalize failed: invalid response");
16125
+ if (finalizeBody.artifact === null) return null;
16126
+ const artifact = parseChatTranscriptArtifact(finalizeBody.artifact);
16127
+ if (!artifact) throw new Error("finalize failed: invalid artifact");
16128
+ return artifact;
16044
16129
  }
16045
16130
 
16046
16131
  // src/services/upload-repo-state.ts
@@ -16747,12 +16832,12 @@ var ChatService = class {
16747
16832
  [...this.chats.entries()].map(([chatId, chat]) => [chatId, this.toSummary(chat)])
16748
16833
  ));
16749
16834
  }
16750
- async flushAllWorkspaceArtifacts() {
16835
+ async flushAllWorkspaceArtifacts(capture) {
16751
16836
  const chatsById = new Map(
16752
16837
  [...this.chats.entries()].map(([chatId, chat]) => [chatId, this.toSummary(chat)])
16753
16838
  );
16754
16839
  const [chatTranscripts, canvas, repoState, engineLogs, chatActivity] = await Promise.all([
16755
- flushAllChatTranscripts(chatsById),
16840
+ flushAllChatTranscripts(chatsById, capture),
16756
16841
  flushAllCanvasItems(),
16757
16842
  flushRepoState(),
16758
16843
  flushAllEngineLogs(),
@@ -18243,7 +18328,9 @@ function createV1Routes(deps) {
18243
18328
  });
18244
18329
  app2.post("/workspace-artifacts/flush-all", async (c) => {
18245
18330
  try {
18246
- const result = await deps.chatService.flushAllWorkspaceArtifacts();
18331
+ const body = await c.req.json().catch(() => void 0);
18332
+ const capture = body && typeof body.captureId === "string" && body.reason === "workspace_sleep" ? body : void 0;
18333
+ const result = await deps.chatService.flushAllWorkspaceArtifacts(capture);
18247
18334
  return c.json(result);
18248
18335
  } catch (error) {
18249
18336
  return c.json(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "replicas-engine",
3
- "version": "0.1.625",
3
+ "version": "0.1.627",
4
4
  "description": "Lightweight API server for Replicas workspaces",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -45,6 +45,20 @@ function providerPath(path) {
45
45
  return path;
46
46
  }
47
47
 
48
+ function providerClient(endpoint, extraOptions = () => ({})) {
49
+ return {
50
+ request: (path, options = {}) => request(endpoint, {
51
+ method: 'POST',
52
+ body: {
53
+ path: providerPath(path),
54
+ method: options.method,
55
+ body: options.body,
56
+ ...extraOptions(options),
57
+ },
58
+ }),
59
+ };
60
+ }
61
+
48
62
  const integrations = {
49
63
  list: () => request('/v1/engine/integrations'),
50
64
  };
@@ -79,26 +93,9 @@ const slack = {
79
93
  }),
80
94
  };
81
95
 
82
- const github = {
83
- request: (path, options = {}) =>
84
- request('/v1/engine/github/api', {
85
- method: 'POST',
86
- body: { path: providerPath(path), method: options.method, body: options.body },
87
- }),
88
- };
96
+ const github = providerClient('/v1/engine/github/api');
89
97
 
90
- const gitlab = {
91
- request: (path, options = {}) =>
92
- request('/v1/engine/gitlab/api', {
93
- method: 'POST',
94
- body: {
95
- path: providerPath(path),
96
- method: options.method,
97
- body: options.body,
98
- host: options.host,
99
- },
100
- }),
101
- };
98
+ const gitlab = providerClient('/v1/engine/gitlab/api', (options) => ({ host: options.host }));
102
99
 
103
100
  const sentry = {
104
101
  request: (path) => request('/v1/engine/sentry/api', { method: 'POST', body: { path } }),
@@ -117,6 +114,8 @@ const turbopuffer = {
117
114
  write: (namespace, params) => turbopufferRequest('write', namespace, params),
118
115
  };
119
116
 
117
+ const planetscale = providerClient('/v1/engine/planetscale/api');
118
+
120
119
  const pangramRequest = (operation, input) =>
121
120
  request('/v1/engine/pangram', { method: 'POST', body: { operation, ...input } });
122
121
  const pangram = {
@@ -124,5 +123,5 @@ const pangram = {
124
123
  plagiarism: (text) => pangramRequest('plagiarism', { text }),
125
124
  };
126
125
 
127
- export const replicas = { integrations, plugins, linear, slack, github, gitlab, sentry, modal, turbopuffer, pangram };
126
+ export const replicas = { integrations, plugins, linear, slack, github, gitlab, sentry, modal, pangram, planetscale, turbopuffer };
128
127
  export default replicas;
@@ -137,6 +137,29 @@ describe('@replicas/sdk', () => {
137
137
  server.stop(true);
138
138
  }
139
139
  });
140
+
141
+ test('calls PlanetScale through the workspace gateway', async () => {
142
+ let observed: { path: string; body: unknown } | null = null;
143
+ const server = Bun.serve({
144
+ port: 0,
145
+ async fetch(request) {
146
+ observed = { path: new URL(request.url).pathname, body: await request.json() };
147
+ return Response.json({ data: [] });
148
+ },
149
+ });
150
+ process.env.REPLICAS_MONOLITH_URL = server.url.toString().replace(/\/$/, '');
151
+ process.env.REPLICAS_ENGINE_SECRET = 'engine-secret';
152
+ process.env.REPLICAS_WORKSPACE_ID = 'workspace-1';
153
+ try {
154
+ await replicas.planetscale.request('/organizations');
155
+ expect(observed).toEqual({
156
+ path: '/v1/engine/planetscale/api',
157
+ body: { path: '/organizations' },
158
+ });
159
+ } finally {
160
+ server.stop(true);
161
+ }
162
+ });
140
163
  });
141
164
 
142
165
  test('analyzes text with Pangram through the workspace gateway', async () => {
@@ -91,6 +91,14 @@ export declare const PLUGIN_CATALOG: readonly [{
91
91
  readonly category: "data";
92
92
  readonly name: "PostHog";
93
93
  readonly description: "Manage projects, events, insights, dashboards, flags, and recordings.";
94
+ }, {
95
+ readonly id: "planetscale";
96
+ readonly toolkit: "planetscale";
97
+ readonly source: "native";
98
+ readonly authType: "oauth";
99
+ readonly category: "data";
100
+ readonly name: "PlanetScale";
101
+ readonly description: "Manage organizations, databases, branches, deploy requests, and Insights.";
94
102
  }, {
95
103
  readonly id: "stripe";
96
104
  readonly toolkit: "stripe";
@@ -121,6 +129,10 @@ export declare const PLUGIN_CATALOG: readonly [{
121
129
  readonly description: "Query and manage turbopuffer namespaces.";
122
130
  }];
123
131
  export type PluginId = (typeof PLUGIN_CATALOG)[number]['id'];
132
+ export type NativePluginId = Extract<(typeof PLUGIN_CATALOG)[number], {
133
+ source: 'native';
134
+ }>['id'];
135
+ export type ComposioPluginId = Exclude<PluginId, NativePluginId>;
124
136
  export type PluginScope = 'organization' | 'personal';
125
137
  export type PluginAuthType = (typeof PLUGIN_CATALOG)[number]['authType'];
126
138
  export type PluginCategory = (typeof PLUGIN_CATALOG)[number]['category'];
@@ -133,6 +145,7 @@ export interface PluginCatalogItem {
133
145
  category: PluginCategory;
134
146
  name: string;
135
147
  description: string;
148
+ source?: 'native';
136
149
  }
137
150
  export interface PluginConnectionSummary {
138
151
  id: string;
@@ -210,7 +223,7 @@ export interface WorkspaceIntegrationsResponse {
210
223
  native: WorkspaceNativeIntegrationStatus;
211
224
  }
212
225
  export interface PluginSearchRequest {
213
- plugin: PluginId;
226
+ plugin: ComposioPluginId;
214
227
  query: string;
215
228
  }
216
229
  export interface PluginToolSchema {
@@ -224,14 +237,14 @@ export interface PluginSearchResponse {
224
237
  tools: PluginToolSchema[];
225
238
  }
226
239
  export interface PluginDescribeRequest {
227
- plugin: PluginId;
240
+ plugin: ComposioPluginId;
228
241
  tools: string[];
229
242
  }
230
243
  export interface PluginDescribeResponse {
231
244
  tools: PluginToolSchema[];
232
245
  }
233
246
  export interface PluginExecuteRequest {
234
- plugin: PluginId;
247
+ plugin: ComposioPluginId;
235
248
  tool: string;
236
249
  arguments?: Record<string, unknown>;
237
250
  }
@@ -240,4 +253,5 @@ export interface PluginExecuteResponse {
240
253
  logId: string;
241
254
  }
242
255
  export declare function isPluginId(value: string): value is PluginId;
256
+ export declare function isComposioPluginId(value: string): value is ComposioPluginId;
243
257
  export declare function isPluginConnectionStatus(value: string): value is PluginConnectionStatus;
@@ -57,6 +57,9 @@ export interface ReplicasSdk {
57
57
  query<T = unknown>(namespace: string, params: Record<string, unknown>): Promise<T>;
58
58
  write<T = unknown>(namespace: string, params: Record<string, unknown>): Promise<T>;
59
59
  };
60
+ planetscale: {
61
+ request<T = unknown>(path: string, options?: ProviderRequestOptions): Promise<T>;
62
+ };
60
63
  pangram: {
61
64
  detect<T = unknown>(input: import('./routes/plugins').PangramAnalyzeRequest): Promise<T>;
62
65
  plagiarism<T = unknown>(text: string): Promise<T>;