@poncho-ai/harness 0.36.4 → 0.37.1

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @poncho-ai/harness@0.36.4 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
2
+ > @poncho-ai/harness@0.37.1 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
3
3
  > node scripts/embed-docs.js && tsup src/index.ts --format esm --dts
4
4
 
5
5
  [embed-docs] Generated poncho-docs.ts with 4 topics
@@ -8,9 +8,9 @@
8
8
  CLI tsup v8.5.1
9
9
  CLI Target: es2022
10
10
  ESM Build start
11
- ESM dist/index.js 387.69 KB
11
+ ESM dist/index.js 389.90 KB
12
12
  ESM dist/isolate-TCWTUVG4.js 47.34 KB
13
- ESM ⚡️ Build success in 182ms
13
+ ESM ⚡️ Build success in 211ms
14
14
  DTS Build start
15
- DTS ⚡️ Build success in 6917ms
15
+ DTS ⚡️ Build success in 6845ms
16
16
  DTS dist/index.d.ts 56.62 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @poncho-ai/harness
2
2
 
3
+ ## 0.37.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`fb61a62`](https://github.com/cesr/poncho-ai/commit/fb61a6259367f0a62d0acd7a20ef2fae93013819) Thanks [@cesr](https://github.com/cesr)! - fix: migration script now discovers and migrates all agent directories instead of only the first one
8
+
9
+ ## 0.37.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`86bc5ac`](https://github.com/cesr/poncho-ai/commit/86bc5ac2a73b80a286228cd9e3b663b50b3d82e7) Thanks [@cesr](https://github.com/cesr)! - perf: promote parentConversationId, pendingApprovals, and channelMeta to dedicated columns so list/summary queries no longer fetch the full data JSONB blob — dramatically reduces database egress
14
+
3
15
  ## 0.36.4
4
16
 
5
17
  ### Patch Changes
package/dist/index.js CHANGED
@@ -3177,6 +3177,36 @@ var migrations = [
3177
3177
  ]
3178
3178
  ];
3179
3179
  }
3180
+ },
3181
+ {
3182
+ version: 4,
3183
+ name: "promote_summary_fields_to_columns",
3184
+ up: (d) => {
3185
+ const jsonType = d === "sqlite" ? "TEXT" : "JSONB";
3186
+ return [
3187
+ `ALTER TABLE conversations ADD COLUMN parent_conversation_id TEXT`,
3188
+ `ALTER TABLE conversations ADD COLUMN has_pending_approvals INTEGER NOT NULL DEFAULT 0`,
3189
+ `ALTER TABLE conversations ADD COLUMN channel_meta ${jsonType}`,
3190
+ // Backfill from data blob
3191
+ ...d === "sqlite" ? [
3192
+ `UPDATE conversations SET
3193
+ parent_conversation_id = json_extract(data, '$.parentConversationId'),
3194
+ has_pending_approvals = CASE
3195
+ WHEN json_array_length(COALESCE(json_extract(data, '$.pendingApprovals'), '[]')) > 0 THEN 1
3196
+ ELSE 0
3197
+ END,
3198
+ channel_meta = json_extract(data, '$.channelMeta')`
3199
+ ] : [
3200
+ `UPDATE conversations SET
3201
+ parent_conversation_id = data->>'parentConversationId',
3202
+ has_pending_approvals = CASE
3203
+ WHEN jsonb_array_length(COALESCE(data->'pendingApprovals', '[]'::jsonb)) > 0 THEN 1
3204
+ ELSE 0
3205
+ END,
3206
+ channel_meta = data->'channelMeta'`
3207
+ ]
3208
+ ];
3209
+ }
3180
3210
  }
3181
3211
  ];
3182
3212
 
@@ -3275,7 +3305,8 @@ var SqlStorageEngine = class {
3275
3305
  const filterTenant = tenantId !== void 0;
3276
3306
  const params = [this.agentId];
3277
3307
  let sql = `SELECT id, title, updated_at, created_at, owner_id, tenant_id,
3278
- message_count, data
3308
+ message_count, parent_conversation_id,
3309
+ has_pending_approvals, channel_meta
3279
3310
  FROM conversations WHERE agent_id = $1`;
3280
3311
  if (filterTenant) {
3281
3312
  sql += ` AND tenant_id = $2`;
@@ -3322,8 +3353,9 @@ var SqlStorageEngine = class {
3322
3353
  const data = JSON.stringify(conv);
3323
3354
  await this.executor.run(
3324
3355
  rewrite(
3325
- `INSERT INTO conversations (id, agent_id, tenant_id, owner_id, title, data, message_count, created_at, updated_at)
3326
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
3356
+ `INSERT INTO conversations (id, agent_id, tenant_id, owner_id, title, data, message_count, created_at, updated_at,
3357
+ parent_conversation_id, has_pending_approvals, channel_meta)
3358
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
3327
3359
  this.dialect
3328
3360
  ),
3329
3361
  [
@@ -3335,7 +3367,10 @@ var SqlStorageEngine = class {
3335
3367
  data,
3336
3368
  0,
3337
3369
  new Date(now2).toISOString(),
3338
- new Date(now2).toISOString()
3370
+ new Date(now2).toISOString(),
3371
+ null,
3372
+ 0,
3373
+ null
3339
3374
  ]
3340
3375
  );
3341
3376
  return conv;
@@ -3358,16 +3393,23 @@ var SqlStorageEngine = class {
3358
3393
  const tid = normalizeTenant2(conversation.tenantId);
3359
3394
  const now2 = new Date(conversation.updatedAt).toISOString();
3360
3395
  const created = new Date(conversation.createdAt).toISOString();
3396
+ const parentConvId = conversation.parentConversationId ?? null;
3397
+ const hasPendingApprovals = (conversation.pendingApprovals?.length ?? 0) > 0 ? 1 : 0;
3398
+ const channelMetaJson = conversation.channelMeta ? JSON.stringify(conversation.channelMeta) : null;
3361
3399
  await this.executor.run(
3362
3400
  rewrite(
3363
3401
  `INSERT INTO conversations (id, agent_id, tenant_id, owner_id, title, data, message_count, created_at, updated_at,
3364
- tool_result_archive, harness_messages, continuation_messages)
3365
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
3402
+ tool_result_archive, harness_messages, continuation_messages,
3403
+ parent_conversation_id, has_pending_approvals, channel_meta)
3404
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
3366
3405
  ${this.dialect.upsert(["id"])}
3367
3406
  data = excluded.data, title = excluded.title, message_count = excluded.message_count,
3368
3407
  updated_at = excluded.updated_at, tenant_id = excluded.tenant_id, owner_id = excluded.owner_id,
3369
3408
  tool_result_archive = excluded.tool_result_archive, harness_messages = excluded.harness_messages,
3370
- continuation_messages = excluded.continuation_messages`,
3409
+ continuation_messages = excluded.continuation_messages,
3410
+ parent_conversation_id = excluded.parent_conversation_id,
3411
+ has_pending_approvals = excluded.has_pending_approvals,
3412
+ channel_meta = excluded.channel_meta`,
3371
3413
  this.dialect
3372
3414
  ),
3373
3415
  [
@@ -3382,7 +3424,10 @@ var SqlStorageEngine = class {
3382
3424
  now2,
3383
3425
  archiveJson,
3384
3426
  harnessJson,
3385
- continuationJson
3427
+ continuationJson,
3428
+ parentConvId,
3429
+ hasPendingApprovals,
3430
+ channelMetaJson
3386
3431
  ]
3387
3432
  );
3388
3433
  },
@@ -3411,7 +3456,8 @@ var SqlStorageEngine = class {
3411
3456
  const pattern = `%${query}%`;
3412
3457
  const params = [this.agentId, pattern, pattern];
3413
3458
  let sql = `SELECT id, title, updated_at, created_at, owner_id, tenant_id,
3414
- message_count, data
3459
+ message_count, parent_conversation_id,
3460
+ has_pending_approvals, channel_meta
3415
3461
  FROM conversations
3416
3462
  WHERE agent_id = $1 AND (title LIKE $2 OR data LIKE $3)`;
3417
3463
  if (filterTenant) {
@@ -3841,8 +3887,8 @@ var SqlStorageEngine = class {
3841
3887
  return data;
3842
3888
  }
3843
3889
  rowToSummary(row) {
3844
- const data = this.parseConversation(row.data);
3845
3890
  const tid = row.tenant_id;
3891
+ const rawChannelMeta = row.channel_meta;
3846
3892
  return {
3847
3893
  conversationId: row.id,
3848
3894
  title: row.title,
@@ -3851,9 +3897,9 @@ var SqlStorageEngine = class {
3851
3897
  ownerId: row.owner_id,
3852
3898
  tenantId: tid === DEFAULT_TENANT2 ? null : tid,
3853
3899
  messageCount: row.message_count,
3854
- hasPendingApprovals: (data.pendingApprovals?.length ?? 0) > 0,
3855
- parentConversationId: data.parentConversationId,
3856
- channelMeta: data.channelMeta
3900
+ hasPendingApprovals: !!row.has_pending_approvals,
3901
+ parentConversationId: row.parent_conversation_id || void 0,
3902
+ channelMeta: rawChannelMeta ? typeof rawChannelMeta === "string" ? JSON.parse(rawChannelMeta) : rawChannelMeta : void 0
3857
3903
  };
3858
3904
  }
3859
3905
  rowToReminder(row) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poncho-ai/harness",
3
- "version": "0.36.4",
3
+ "version": "0.37.1",
4
4
  "description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
5
5
  "repository": {
6
6
  "type": "git",
@@ -106,30 +106,26 @@ async function readJsonSafe(filePath) {
106
106
  }
107
107
  }
108
108
 
109
- async function findAgentDir(workingDir) {
109
+ async function findAgentDirs(workingDir) {
110
110
  const ponchoDir = resolve(workingDir, ".poncho");
111
+ const results = [];
111
112
  try {
112
113
  const entries = await readdir(ponchoDir, { withFileTypes: true });
113
114
  for (const e of entries) {
114
115
  if (e.isDirectory() && !e.name.startsWith(".")) {
115
- return { dir: resolve(ponchoDir, e.name), id: e.name };
116
+ results.push({ dir: resolve(ponchoDir, e.name), id: e.name });
116
117
  }
117
118
  }
118
119
  } catch { /* no .poncho dir */ }
119
- return undefined;
120
+ return results;
120
121
  }
121
122
 
122
123
  // ---------------------------------------------------------------------------
123
124
  // Read from local
124
125
  // ---------------------------------------------------------------------------
125
126
 
126
- async function readLocal(workingDir) {
127
- const agent = await findAgentDir(workingDir);
128
- if (!agent) {
129
- console.error("No .poncho agent directory found in", workingDir);
130
- process.exit(1);
131
- }
132
- console.log(`Found local agent: ${agent.id} at ${agent.dir}`);
127
+ async function readLocalAgent(agent) {
128
+ console.log(` Reading agent: ${agent.id} at ${agent.dir}`);
133
129
 
134
130
  const data = { agentId: agent.id, conversations: [], memories: [], todos: [], reminders: [] };
135
131
 
@@ -189,6 +185,29 @@ async function readLocal(workingDir) {
189
185
  return data;
190
186
  }
191
187
 
188
+ async function readLocal(workingDir) {
189
+ const agents = await findAgentDirs(workingDir);
190
+ if (agents.length === 0) {
191
+ console.error("No .poncho agent directory found in", workingDir);
192
+ process.exit(1);
193
+ }
194
+
195
+ // If --agent-id is specified, filter to that agent
196
+ const filtered = AGENT_ID ? agents.filter((a) => a.id === AGENT_ID) : agents;
197
+ if (filtered.length === 0) {
198
+ console.error(`Agent "${AGENT_ID}" not found. Available agents: ${agents.map((a) => a.id).join(", ")}`);
199
+ process.exit(1);
200
+ }
201
+
202
+ console.log(`Found ${filtered.length} agent(s) to migrate`);
203
+
204
+ const results = [];
205
+ for (const agent of filtered) {
206
+ results.push(await readLocalAgent(agent));
207
+ }
208
+ return results;
209
+ }
210
+
192
211
  // ---------------------------------------------------------------------------
193
212
  // Read from Upstash
194
213
  // ---------------------------------------------------------------------------
@@ -323,9 +342,12 @@ async function readUpstash(agentId) {
323
342
  async function readFromEngine(sourceProvider, agentId) {
324
343
  if (!agentId) {
325
344
  // Try to detect from .poncho directory
326
- const agent = await findAgentDir(WORKING_DIR);
327
- if (agent) agentId = agent.id;
328
- else {
345
+ const agents = await findAgentDirs(WORKING_DIR);
346
+ if (agents.length === 1) agentId = agents[0].id;
347
+ else if (agents.length > 1) {
348
+ console.error(`Multiple agents found: ${agents.map((a) => a.id).join(", ")}. Use --agent-id to specify one.`);
349
+ process.exit(1);
350
+ } else {
329
351
  console.error("--agent-id is required for engine source (or run from a project with .poncho/)");
330
352
  process.exit(1);
331
353
  }
@@ -513,40 +535,43 @@ async function main() {
513
535
  console.log(`Working dir: ${WORKING_DIR}`);
514
536
  if (DRY_RUN) console.log("(dry run — no data will be written)\n");
515
537
 
516
- // Read source
517
- let data;
538
+ // Read source — local returns an array (one per agent), others return a single object
539
+ let dataList;
518
540
  if (SOURCE === "local") {
519
- data = await readLocal(WORKING_DIR);
541
+ dataList = await readLocal(WORKING_DIR);
520
542
  } else if (SOURCE === "upstash") {
521
- data = await readUpstash(AGENT_ID);
543
+ dataList = [await readUpstash(AGENT_ID)];
522
544
  } else if (SOURCE === "sqlite" || SOURCE === "postgresql") {
523
- data = await readFromEngine(SOURCE, AGENT_ID);
545
+ dataList = [await readFromEngine(SOURCE, AGENT_ID)];
524
546
  } else {
525
547
  console.error(`Unknown source: ${SOURCE}. Use "local", "upstash", "sqlite", or "postgresql".`);
526
548
  process.exit(1);
527
549
  }
528
550
 
529
- console.log(`\nRead from ${SOURCE}:`);
530
- console.log(` Conversations: ${data.conversations.length}`);
531
- console.log(` Memories: ${data.memories?.length ?? 0}`);
532
- console.log(` Todo lists: ${data.todos.length}`);
533
- console.log(` Reminders: ${data.reminders.length}`);
534
- if (data.vfsFiles?.length) console.log(` VFS files: ${data.vfsFiles.length}`);
551
+ for (const data of dataList) {
552
+ console.log(`\nAgent: ${data.agentId}`);
553
+ console.log(` Read from ${SOURCE}:`);
554
+ console.log(` Conversations: ${data.conversations.length}`);
555
+ console.log(` Memories: ${data.memories?.length ?? 0}`);
556
+ console.log(` Todo lists: ${data.todos.length}`);
557
+ console.log(` Reminders: ${data.reminders.length}`);
558
+ if (data.vfsFiles?.length) console.log(` VFS files: ${data.vfsFiles.length}`);
559
+
560
+ if (data.conversations.length === 0 && !data.memories?.length && data.todos.length === 0 && data.reminders.length === 0 && !data.vfsFiles?.length) {
561
+ console.log(" Nothing to migrate for this agent.");
562
+ continue;
563
+ }
535
564
 
536
- if (data.conversations.length === 0 && !data.memories?.length && data.todos.length === 0 && data.reminders.length === 0 && !data.vfsFiles?.length) {
537
- console.log("\nNothing to migrate.");
538
- process.exit(0);
539
- }
565
+ const result = await writeToEngine(data);
540
566
 
541
- // Write to target
542
- const result = await writeToEngine(data);
567
+ console.log(` ${DRY_RUN ? "Would import" : "Imported"} to ${TARGET}:`);
568
+ console.log(` Conversations: ${result.convCount}`);
569
+ console.log(` Memories: ${result.memoryCount}`);
570
+ console.log(` Todos: ${result.todoCount}`);
571
+ console.log(` Reminders: ${result.reminderCount}`);
572
+ if (result.vfsCount) console.log(` VFS files: ${result.vfsCount}`);
573
+ }
543
574
 
544
- console.log(`\n${DRY_RUN ? "Would import" : "Imported"} to ${TARGET}:`);
545
- console.log(` Conversations: ${result.convCount}`);
546
- console.log(` Memories: ${result.memoryCount}`);
547
- console.log(` Todos: ${result.todoCount}`);
548
- console.log(` Reminders: ${result.reminderCount}`);
549
- if (result.vfsCount) console.log(` VFS files: ${result.vfsCount}`);
550
575
  console.log("\nDone!");
551
576
  }
552
577
 
@@ -142,4 +142,36 @@ export const migrations: Migration[] = [
142
142
  ];
143
143
  },
144
144
  },
145
+ {
146
+ version: 4,
147
+ name: "promote_summary_fields_to_columns",
148
+ up: (d) => {
149
+ const jsonType = d === "sqlite" ? "TEXT" : "JSONB";
150
+ return [
151
+ `ALTER TABLE conversations ADD COLUMN parent_conversation_id TEXT`,
152
+ `ALTER TABLE conversations ADD COLUMN has_pending_approvals INTEGER NOT NULL DEFAULT 0`,
153
+ `ALTER TABLE conversations ADD COLUMN channel_meta ${jsonType}`,
154
+ // Backfill from data blob
155
+ ...(d === "sqlite"
156
+ ? [
157
+ `UPDATE conversations SET
158
+ parent_conversation_id = json_extract(data, '$.parentConversationId'),
159
+ has_pending_approvals = CASE
160
+ WHEN json_array_length(COALESCE(json_extract(data, '$.pendingApprovals'), '[]')) > 0 THEN 1
161
+ ELSE 0
162
+ END,
163
+ channel_meta = json_extract(data, '$.channelMeta')`,
164
+ ]
165
+ : [
166
+ `UPDATE conversations SET
167
+ parent_conversation_id = data->>'parentConversationId',
168
+ has_pending_approvals = CASE
169
+ WHEN jsonb_array_length(COALESCE(data->'pendingApprovals', '[]'::jsonb)) > 0 THEN 1
170
+ ELSE 0
171
+ END,
172
+ channel_meta = data->'channelMeta'`,
173
+ ]),
174
+ ];
175
+ },
176
+ },
145
177
  ];
@@ -189,7 +189,8 @@ export abstract class SqlStorageEngine implements StorageEngine {
189
189
  const filterTenant = tenantId !== undefined;
190
190
  const params: unknown[] = [this.agentId];
191
191
  let sql = `SELECT id, title, updated_at, created_at, owner_id, tenant_id,
192
- message_count, data
192
+ message_count, parent_conversation_id,
193
+ has_pending_approvals, channel_meta
193
194
  FROM conversations WHERE agent_id = $1`;
194
195
  if (filterTenant) {
195
196
  sql += ` AND tenant_id = $2`;
@@ -258,8 +259,9 @@ export abstract class SqlStorageEngine implements StorageEngine {
258
259
  const data = JSON.stringify(conv);
259
260
  await this.executor.run(
260
261
  rewrite(
261
- `INSERT INTO conversations (id, agent_id, tenant_id, owner_id, title, data, message_count, created_at, updated_at)
262
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
262
+ `INSERT INTO conversations (id, agent_id, tenant_id, owner_id, title, data, message_count, created_at, updated_at,
263
+ parent_conversation_id, has_pending_approvals, channel_meta)
264
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`,
263
265
  this.dialect,
264
266
  ),
265
267
  [
@@ -272,6 +274,9 @@ export abstract class SqlStorageEngine implements StorageEngine {
272
274
  0,
273
275
  new Date(now).toISOString(),
274
276
  new Date(now).toISOString(),
277
+ null,
278
+ 0,
279
+ null,
275
280
  ],
276
281
  );
277
282
  return conv;
@@ -296,16 +301,23 @@ export abstract class SqlStorageEngine implements StorageEngine {
296
301
  const tid = normalizeTenant(conversation.tenantId);
297
302
  const now = new Date(conversation.updatedAt).toISOString();
298
303
  const created = new Date(conversation.createdAt).toISOString();
304
+ const parentConvId = conversation.parentConversationId ?? null;
305
+ const hasPendingApprovals = (conversation.pendingApprovals?.length ?? 0) > 0 ? 1 : 0;
306
+ const channelMetaJson = conversation.channelMeta ? JSON.stringify(conversation.channelMeta) : null;
299
307
  await this.executor.run(
300
308
  rewrite(
301
309
  `INSERT INTO conversations (id, agent_id, tenant_id, owner_id, title, data, message_count, created_at, updated_at,
302
- tool_result_archive, harness_messages, continuation_messages)
303
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
310
+ tool_result_archive, harness_messages, continuation_messages,
311
+ parent_conversation_id, has_pending_approvals, channel_meta)
312
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
304
313
  ${this.dialect.upsert(["id"])}
305
314
  data = excluded.data, title = excluded.title, message_count = excluded.message_count,
306
315
  updated_at = excluded.updated_at, tenant_id = excluded.tenant_id, owner_id = excluded.owner_id,
307
316
  tool_result_archive = excluded.tool_result_archive, harness_messages = excluded.harness_messages,
308
- continuation_messages = excluded.continuation_messages`,
317
+ continuation_messages = excluded.continuation_messages,
318
+ parent_conversation_id = excluded.parent_conversation_id,
319
+ has_pending_approvals = excluded.has_pending_approvals,
320
+ channel_meta = excluded.channel_meta`,
309
321
  this.dialect,
310
322
  ),
311
323
  [
@@ -321,6 +333,9 @@ export abstract class SqlStorageEngine implements StorageEngine {
321
333
  archiveJson,
322
334
  harnessJson,
323
335
  continuationJson,
336
+ parentConvId,
337
+ hasPendingApprovals,
338
+ channelMetaJson,
324
339
  ],
325
340
  );
326
341
  },
@@ -359,7 +374,8 @@ export abstract class SqlStorageEngine implements StorageEngine {
359
374
  // SQLite uses positional ? so we can't reuse $2, need separate params
360
375
  const params: unknown[] = [this.agentId, pattern, pattern];
361
376
  let sql = `SELECT id, title, updated_at, created_at, owner_id, tenant_id,
362
- message_count, data
377
+ message_count, parent_conversation_id,
378
+ has_pending_approvals, channel_meta
363
379
  FROM conversations
364
380
  WHERE agent_id = $1 AND (title LIKE $2 OR data LIKE $3)`;
365
381
  if (filterTenant) {
@@ -882,8 +898,8 @@ export abstract class SqlStorageEngine implements StorageEngine {
882
898
  }
883
899
 
884
900
  private rowToSummary(row: QueryRow): ConversationSummary {
885
- const data = this.parseConversation(row.data);
886
901
  const tid = row.tenant_id as string;
902
+ const rawChannelMeta = row.channel_meta;
887
903
  return {
888
904
  conversationId: row.id as string,
889
905
  title: row.title as string,
@@ -892,9 +908,11 @@ export abstract class SqlStorageEngine implements StorageEngine {
892
908
  ownerId: row.owner_id as string,
893
909
  tenantId: tid === DEFAULT_TENANT ? null : tid,
894
910
  messageCount: row.message_count as number,
895
- hasPendingApprovals: (data.pendingApprovals?.length ?? 0) > 0,
896
- parentConversationId: data.parentConversationId,
897
- channelMeta: data.channelMeta,
911
+ hasPendingApprovals: !!(row.has_pending_approvals),
912
+ parentConversationId: (row.parent_conversation_id as string) || undefined,
913
+ channelMeta: rawChannelMeta
914
+ ? (typeof rawChannelMeta === "string" ? JSON.parse(rawChannelMeta) : rawChannelMeta)
915
+ : undefined,
898
916
  };
899
917
  }
900
918