@poncho-ai/harness 0.55.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2443,7 +2443,7 @@ var ponchoDocsTool = defineTool({
2443
2443
  import { randomUUID as randomUUID5 } from "crypto";
2444
2444
  import { readFile as readFile9 } from "fs/promises";
2445
2445
  import { resolve as resolve11 } from "path";
2446
- import { defineTool as defineTool12, getTextContent as getTextContent2, createLogger as createLogger6, formatError as fmtErr, url as urlColor } from "@poncho-ai/sdk";
2446
+ import { defineTool as defineTool12, getTextContent as getTextContent2, createLogger as createLogger7, formatError as fmtErr, url as urlColor } from "@poncho-ai/sdk";
2447
2447
 
2448
2448
  // src/upload-store.ts
2449
2449
  import { createHash as createHash2 } from "crypto";
@@ -2776,6 +2776,87 @@ var createUploadStore = async (config, workingDir) => {
2776
2776
 
2777
2777
  // src/storage/memory-engine.ts
2778
2778
  import { randomUUID as randomUUID3 } from "crypto";
2779
+
2780
+ // src/storage/entries.ts
2781
+ import { createLogger as createLogger2 } from "@poncho-ai/sdk";
2782
+ var entriesReadLog = createLogger2("entries-read");
2783
+ function buildLlmContext(entries) {
2784
+ let latestCompaction;
2785
+ for (const e of entries) {
2786
+ if (e.type === "compaction" && (!latestCompaction || e.seq > latestCompaction.seq)) {
2787
+ latestCompaction = e;
2788
+ }
2789
+ }
2790
+ const harnessMsgs = entries.filter(
2791
+ (e) => e.type === "harness_message"
2792
+ );
2793
+ if (latestCompaction) {
2794
+ const kept = harnessMsgs.filter((e) => e.seq >= latestCompaction.firstKeptSeq).map((e) => e.message);
2795
+ return [latestCompaction.summaryMessage, ...kept];
2796
+ }
2797
+ return harnessMsgs.map((e) => e.message);
2798
+ }
2799
+ function buildDisplaySnapshot(entries, tailN) {
2800
+ const amendmentsByTarget = /* @__PURE__ */ new Map();
2801
+ for (const e of entries) {
2802
+ if (e.type === "assistant_amendment") {
2803
+ const list = amendmentsByTarget.get(e.targetEntryId) ?? [];
2804
+ list.push(e);
2805
+ amendmentsByTarget.set(e.targetEntryId, list);
2806
+ }
2807
+ }
2808
+ const built = [];
2809
+ for (const e of entries) {
2810
+ if (e.type === "user_message") {
2811
+ if (e.hidden) continue;
2812
+ built.push({ seq: e.seq, message: e.message });
2813
+ } else if (e.type === "assistant_message") {
2814
+ let content = typeof e.message.content === "string" ? e.message.content : "";
2815
+ const amendments = amendmentsByTarget.get(e.id);
2816
+ if (amendments) {
2817
+ for (const a of amendments.sort((x, y) => x.seq - y.seq)) {
2818
+ if (a.appendText) content += a.appendText;
2819
+ }
2820
+ }
2821
+ built.push({ seq: e.seq, message: { ...e.message, content } });
2822
+ }
2823
+ }
2824
+ const total = built.length;
2825
+ const tail = tailN >= total ? built : built.slice(total - tailN);
2826
+ return {
2827
+ messages: tail.map((b) => b.message),
2828
+ totalMessages: total,
2829
+ headSeq: tail.length > 0 ? tail[0].seq : null
2830
+ };
2831
+ }
2832
+ function getPendingSubagentResults(entries) {
2833
+ const consumed = /* @__PURE__ */ new Set();
2834
+ for (const e of entries) {
2835
+ if (e.type === "callback_started") {
2836
+ for (const s of e.consumedSeqs) consumed.add(s);
2837
+ }
2838
+ }
2839
+ return entries.filter((e) => e.type === "subagent_result").filter((e) => !consumed.has(e.seq)).map((e) => e.result);
2840
+ }
2841
+ var FULL_TRANSCRIPT_TAIL = 1e5;
2842
+ async function rebuildConversationFromEntries(conversation, readEntries) {
2843
+ if (process.env.PONCHO_READ_ENTRIES === "0") return conversation;
2844
+ try {
2845
+ const entries = await readEntries(conversation.conversationId);
2846
+ if (entries.length === 0) return conversation;
2847
+ conversation._harnessMessages = buildLlmContext(entries);
2848
+ conversation.messages = buildDisplaySnapshot(entries, FULL_TRANSCRIPT_TAIL).messages;
2849
+ conversation.pendingSubagentResults = getPendingSubagentResults(entries);
2850
+ return conversation;
2851
+ } catch (err) {
2852
+ entriesReadLog.warn(
2853
+ `[entries-read] ${conversation.conversationId} rebuild failed, using blob: ${err instanceof Error ? err.message : String(err)}`
2854
+ );
2855
+ return conversation;
2856
+ }
2857
+ }
2858
+
2859
+ // src/storage/memory-engine.ts
2779
2860
  var DEFAULT_TENANT = "__default__";
2780
2861
  var DEFAULT_OWNER = "local-owner";
2781
2862
  var normalizeTenant = (tenantId) => tenantId ?? DEFAULT_TENANT;
@@ -2789,6 +2870,8 @@ var InMemoryEngine = class {
2789
2870
  agentId;
2790
2871
  // Conversation data
2791
2872
  convs = /* @__PURE__ */ new Map();
2873
+ // Append-only conversation entries (Phase 3 substrate)
2874
+ entries = /* @__PURE__ */ new Map();
2792
2875
  // Memory data
2793
2876
  mem = /* @__PURE__ */ new Map();
2794
2877
  // Todos data
@@ -2824,12 +2907,22 @@ var InMemoryEngine = class {
2824
2907
  return results;
2825
2908
  },
2826
2909
  get: async (conversationId) => {
2827
- return this.convs.get(conversationId);
2910
+ const c = this.convs.get(conversationId);
2911
+ if (!c) return void 0;
2912
+ return rebuildConversationFromEntries(
2913
+ { ...c },
2914
+ (id) => this.conversations.readEntries(id)
2915
+ );
2828
2916
  },
2829
2917
  // In-memory storage has no separate archive blob, so both variants
2830
2918
  // return the same conversation object.
2831
2919
  getWithArchive: async (conversationId) => {
2832
- return this.convs.get(conversationId);
2920
+ const c = this.convs.get(conversationId);
2921
+ if (!c) return void 0;
2922
+ return rebuildConversationFromEntries(
2923
+ { ...c },
2924
+ (id) => this.conversations.readEntries(id)
2925
+ );
2833
2926
  },
2834
2927
  getStatusSnapshot: async (conversationId) => {
2835
2928
  const c = this.convs.get(conversationId);
@@ -2918,6 +3011,30 @@ var InMemoryEngine = class {
2918
3011
  }
2919
3012
  results.sort((a, b) => b.updatedAt - a.updatedAt);
2920
3013
  return results;
3014
+ },
3015
+ appendEntries: async (conversationId, _agentId, _tenantId, entries) => {
3016
+ const list = this.entries.get(conversationId) ?? [];
3017
+ let nextSeq = list.reduce((max, e) => e.seq > max ? e.seq : max, 0) + 1;
3018
+ const now2 = Date.now();
3019
+ const stored = entries.map(
3020
+ (e) => ({ ...e, seq: nextSeq++, createdAt: now2 })
3021
+ );
3022
+ this.entries.set(conversationId, [...list, ...stored]);
3023
+ return stored;
3024
+ },
3025
+ readEntries: async (conversationId, opts) => {
3026
+ let list = (this.entries.get(conversationId) ?? []).slice().sort((a, b) => a.seq - b.seq);
3027
+ if (opts?.types && opts.types.length > 0) {
3028
+ const allowed = new Set(opts.types);
3029
+ list = list.filter((e) => allowed.has(e.type));
3030
+ }
3031
+ if (typeof opts?.afterSeq === "number") {
3032
+ list = list.filter((e) => e.seq > opts.afterSeq);
3033
+ }
3034
+ if (typeof opts?.limit === "number") {
3035
+ list = list.slice(0, opts.limit);
3036
+ }
3037
+ return list;
2921
3038
  }
2922
3039
  };
2923
3040
  // -----------------------------------------------------------------------
@@ -3266,7 +3383,7 @@ import { dirname as dirname2, resolve as resolve6 } from "path";
3266
3383
 
3267
3384
  // src/storage/sql-dialect.ts
3268
3385
  import { randomUUID as randomUUID4 } from "crypto";
3269
- import { createLogger as createLogger2 } from "@poncho-ai/sdk";
3386
+ import { createLogger as createLogger3 } from "@poncho-ai/sdk";
3270
3387
 
3271
3388
  // src/storage/schema.ts
3272
3389
  var migrations = [
@@ -3457,11 +3574,41 @@ var migrations = [
3457
3574
  `ALTER TABLE reminders ALTER COLUMN scheduled_at TYPE BIGINT USING scheduled_at::bigint`
3458
3575
  ];
3459
3576
  }
3577
+ },
3578
+ {
3579
+ version: 8,
3580
+ name: "conversation_entries",
3581
+ // Append-only conversation log (Phase 3 substrate). Additive: no
3582
+ // existing table or behavior changes. `seq` is a per-conversation
3583
+ // monotonic order assigned by the application (NOT an autoincrement
3584
+ // serial), so the same seq space restarts at 1 for every conversation.
3585
+ // The UNIQUE (conversation_id, seq) constraint backstops the
3586
+ // app-assigned ordering against concurrent writers.
3587
+ up: (d) => {
3588
+ const jsonType = d === "sqlite" ? "TEXT" : "JSONB";
3589
+ const tsDefault = d === "sqlite" ? "datetime('now')" : "NOW()";
3590
+ const autoTs = `DEFAULT (${tsDefault})`;
3591
+ return [
3592
+ `CREATE TABLE IF NOT EXISTS conversation_entries (
3593
+ seq INTEGER NOT NULL,
3594
+ id TEXT NOT NULL UNIQUE,
3595
+ agent_id TEXT NOT NULL,
3596
+ tenant_id TEXT NOT NULL DEFAULT '__default__',
3597
+ conversation_id TEXT NOT NULL,
3598
+ type TEXT NOT NULL,
3599
+ payload ${jsonType} NOT NULL,
3600
+ created_at TIMESTAMP ${autoTs},
3601
+ UNIQUE (conversation_id, seq)
3602
+ )`,
3603
+ `CREATE INDEX IF NOT EXISTS idx_conversation_entries_seq
3604
+ ON conversation_entries (conversation_id, seq)`
3605
+ ];
3606
+ }
3460
3607
  }
3461
3608
  ];
3462
3609
 
3463
3610
  // src/storage/sql-dialect.ts
3464
- var egressLog = createLogger2("egress");
3611
+ var egressLog = createLogger3("egress");
3465
3612
  var sqliteDialect = {
3466
3613
  tag: "sqlite",
3467
3614
  param: () => "?",
@@ -3651,7 +3798,10 @@ var SqlStorageEngine = class {
3651
3798
  if (row.continuation_messages) {
3652
3799
  conv._continuationMessages = typeof row.continuation_messages === "string" ? JSON.parse(row.continuation_messages) : row.continuation_messages;
3653
3800
  }
3654
- return conv;
3801
+ return rebuildConversationFromEntries(
3802
+ conv,
3803
+ (id) => this.conversations.readEntries(id)
3804
+ );
3655
3805
  },
3656
3806
  getStatusSnapshot: async (conversationId) => {
3657
3807
  const runStatusExpr = this.dialect.tag === "sqlite" ? "json_extract(data, '$.runStatus')" : "data->>'runStatus'";
@@ -3701,7 +3851,10 @@ var SqlStorageEngine = class {
3701
3851
  if (row.continuation_messages) {
3702
3852
  conv._continuationMessages = typeof row.continuation_messages === "string" ? JSON.parse(row.continuation_messages) : row.continuation_messages;
3703
3853
  }
3704
- return conv;
3854
+ return rebuildConversationFromEntries(
3855
+ conv,
3856
+ (id) => this.conversations.readEntries(id)
3857
+ );
3705
3858
  },
3706
3859
  create: async (ownerId, title, tenantId, init) => {
3707
3860
  const id = randomUUID4();
@@ -3880,6 +4033,87 @@ var SqlStorageEngine = class {
3880
4033
  parentConversationId
3881
4034
  ]);
3882
4035
  return rows.map((r) => this.rowToSummary(r));
4036
+ },
4037
+ appendEntries: async (conversationId, agentId, tenantId, entries) => {
4038
+ if (entries.length === 0) return [];
4039
+ const tid = normalizeTenant2(tenantId);
4040
+ const maxAttempts = 3;
4041
+ let lastErr;
4042
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
4043
+ const stored = [];
4044
+ try {
4045
+ await this.executor.transaction(async () => {
4046
+ stored.length = 0;
4047
+ const row = await this.executor.get(
4048
+ rewrite(
4049
+ "SELECT MAX(seq) AS max_seq FROM conversation_entries WHERE conversation_id = $1",
4050
+ this.dialect
4051
+ ),
4052
+ [conversationId]
4053
+ );
4054
+ let nextSeq = Number(row?.max_seq ?? 0) + 1;
4055
+ const now2 = Date.now();
4056
+ for (const entry of entries) {
4057
+ const seq = nextSeq++;
4058
+ const createdAt = now2;
4059
+ const payload = JSON.stringify(entry);
4060
+ await this.executor.run(
4061
+ rewrite(
4062
+ `INSERT INTO conversation_entries
4063
+ (seq, id, agent_id, tenant_id, conversation_id, type, payload, created_at)
4064
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
4065
+ this.dialect
4066
+ ),
4067
+ [
4068
+ seq,
4069
+ entry.id,
4070
+ agentId,
4071
+ tid,
4072
+ conversationId,
4073
+ entry.type,
4074
+ payload,
4075
+ new Date(createdAt).toISOString()
4076
+ ]
4077
+ );
4078
+ stored.push({ ...entry, seq, createdAt });
4079
+ }
4080
+ });
4081
+ return stored;
4082
+ } catch (err) {
4083
+ lastErr = err;
4084
+ }
4085
+ }
4086
+ throw lastErr;
4087
+ },
4088
+ readEntries: async (conversationId, opts) => {
4089
+ const params = [conversationId];
4090
+ let sql = "SELECT seq, id, payload, created_at FROM conversation_entries WHERE conversation_id = $1";
4091
+ if (opts?.types && opts.types.length > 0) {
4092
+ const placeholders = opts.types.map(
4093
+ (_t, i) => `$${params.length + 1 + i}`
4094
+ );
4095
+ sql += ` AND type IN (${placeholders.join(", ")})`;
4096
+ params.push(...opts.types);
4097
+ }
4098
+ if (typeof opts?.afterSeq === "number") {
4099
+ sql += ` AND seq > $${params.length + 1}`;
4100
+ params.push(opts.afterSeq);
4101
+ }
4102
+ sql += " ORDER BY seq ASC";
4103
+ if (typeof opts?.limit === "number") {
4104
+ sql += ` LIMIT $${params.length + 1}`;
4105
+ params.push(opts.limit);
4106
+ }
4107
+ const rows = await this.executor.all(rewrite(sql, this.dialect), params);
4108
+ return rows.map((r) => {
4109
+ const parsed = typeof r.payload === "string" ? JSON.parse(r.payload) : r.payload;
4110
+ return {
4111
+ ...parsed,
4112
+ id: r.id,
4113
+ seq: Number(r.seq),
4114
+ createdAt: new Date(r.created_at).getTime()
4115
+ };
4116
+ });
3883
4117
  }
3884
4118
  };
3885
4119
  // -----------------------------------------------------------------------
@@ -4712,7 +4946,9 @@ function createConversationStoreFromEngine(engine) {
4712
4946
  delete: (conversationId) => engine.conversations.delete(conversationId),
4713
4947
  appendSubagentResult: (conversationId, result) => engine.conversations.appendSubagentResult(conversationId, result),
4714
4948
  clearCallbackLock: (conversationId) => engine.conversations.clearCallbackLock(conversationId),
4715
- listThreads: (parentConversationId) => engine.conversations.listThreads(parentConversationId)
4949
+ listThreads: (parentConversationId) => engine.conversations.listThreads(parentConversationId),
4950
+ appendEntries: (conversationId, agentId, tenantId, entries) => engine.conversations.appendEntries(conversationId, agentId, tenantId, entries),
4951
+ readEntries: (conversationId, opts) => engine.conversations.readEntries(conversationId, opts)
4716
4952
  };
4717
4953
  }
4718
4954
  function createMemoryStoreFromEngine(engine, tenantId) {
@@ -6441,8 +6677,8 @@ var createReminderTools = (store) => [
6441
6677
  ];
6442
6678
 
6443
6679
  // src/mcp.ts
6444
- import { createLogger as createLogger3 } from "@poncho-ai/sdk";
6445
- var mcpLog = createLogger3("mcp");
6680
+ import { createLogger as createLogger4 } from "@poncho-ai/sdk";
6681
+ var mcpLog = createLogger4("mcp");
6446
6682
  var McpHttpError = class extends Error {
6447
6683
  status;
6448
6684
  constructor(status, message) {
@@ -7435,8 +7671,8 @@ var createModelProvider = (provider, config) => {
7435
7671
  import { readFile as readFile8, readdir as readdir3, stat as stat2 } from "fs/promises";
7436
7672
  import { dirname as dirname5, resolve as resolve9, normalize as normalize2 } from "path";
7437
7673
  import YAML3 from "yaml";
7438
- import { createLogger as createLogger4 } from "@poncho-ai/sdk";
7439
- var logger = createLogger4("skills");
7674
+ import { createLogger as createLogger5 } from "@poncho-ai/sdk";
7675
+ var logger = createLogger5("skills");
7440
7676
  var DEFAULT_SKILL_DIRS = ["skills"];
7441
7677
  var resolveSkillDirs = (workingDir, extraPaths) => {
7442
7678
  const dirs = [...DEFAULT_SKILL_DIRS];
@@ -8552,9 +8788,9 @@ import { NodeTracerProvider, BatchSpanProcessor } from "@opentelemetry/sdk-trace
8552
8788
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
8553
8789
 
8554
8790
  // src/telemetry.ts
8555
- import { createLogger as createLogger5 } from "@poncho-ai/sdk";
8556
- var eventLog = createLogger5("event");
8557
- var telemetryLog = createLogger5("telemetry");
8791
+ import { createLogger as createLogger6 } from "@poncho-ai/sdk";
8792
+ var eventLog = createLogger6("event");
8793
+ var telemetryLog = createLogger6("telemetry");
8558
8794
  var MAX_FIELD_LENGTH = 200;
8559
8795
  var OMIT_FROM_LOG = /* @__PURE__ */ new Set(["continuationMessages", "_harnessMessages", "messages", "compactedHistory"]);
8560
8796
  function sanitizeEventForLog(event) {
@@ -8725,11 +8961,11 @@ var ToolDispatcher = class {
8725
8961
  };
8726
8962
 
8727
8963
  // src/harness.ts
8728
- var harnessLog = createLogger6("harness");
8729
- var telemetryLog2 = createLogger6("telemetry");
8730
- var costLog = createLogger6("cost");
8731
- var mcpLog2 = createLogger6("mcp");
8732
- var modelLog = createLogger6("model");
8964
+ var harnessLog = createLogger7("harness");
8965
+ var telemetryLog2 = createLogger7("telemetry");
8966
+ var costLog = createLogger7("cost");
8967
+ var mcpLog2 = createLogger7("mcp");
8968
+ var modelLog = createLogger7("model");
8733
8969
  function formatOtlpError(err) {
8734
8970
  if (!(err instanceof Error)) return String(err);
8735
8971
  const parts = [];
@@ -9701,7 +9937,7 @@ var AgentHarness = class _AgentHarness {
9701
9937
  const key = `${effectiveTenant}:${skipped.name}`;
9702
9938
  if (this.vfsSkillCollisionWarnings.has(key)) return;
9703
9939
  this.vfsSkillCollisionWarnings.add(key);
9704
- createLogger6("skills").warn(
9940
+ createLogger7("skills").warn(
9705
9941
  `VFS skill "${skipped.name}" for tenant ${effectiveTenant} ignored: a repo skill with the same name takes precedence.`
9706
9942
  );
9707
9943
  });
@@ -9954,7 +10190,7 @@ var AgentHarness = class _AgentHarness {
9954
10190
  this.agentFileFingerprint = rawContent;
9955
10191
  return true;
9956
10192
  } catch (error) {
9957
- createLogger6("agent").warn(`failed to refresh AGENT.md in dev: ${fmtErr(error)}`);
10193
+ createLogger7("agent").warn(`failed to refresh AGENT.md in dev: ${fmtErr(error)}`);
9958
10194
  return false;
9959
10195
  }
9960
10196
  }
@@ -10002,7 +10238,7 @@ var AgentHarness = class _AgentHarness {
10002
10238
  await this.refreshMcpTools("skills:changed");
10003
10239
  return true;
10004
10240
  } catch (error) {
10005
- createLogger6("skills").warn(`failed to refresh skills in dev: ${fmtErr(error)}`);
10241
+ createLogger7("skills").warn(`failed to refresh skills in dev: ${fmtErr(error)}`);
10006
10242
  return false;
10007
10243
  }
10008
10244
  }
@@ -10112,7 +10348,7 @@ var AgentHarness = class _AgentHarness {
10112
10348
  }
10113
10349
  if (config?.browser) {
10114
10350
  await this.initBrowserTools(config).catch((e) => {
10115
- createLogger6("browser").warn(`failed to load browser tools: ${fmtErr(e)}`);
10351
+ createLogger7("browser").warn(`failed to load browser tools: ${fmtErr(e)}`);
10116
10352
  });
10117
10353
  }
10118
10354
  const stateConfig = resolveStateConfig(config);
@@ -11973,6 +12209,7 @@ var InMemoryStateStore = class {
11973
12209
  };
11974
12210
  var InMemoryConversationStore = class {
11975
12211
  conversations = /* @__PURE__ */ new Map();
12212
+ entries = /* @__PURE__ */ new Map();
11976
12213
  ttlMs;
11977
12214
  constructor(ttlSeconds) {
11978
12215
  this.ttlMs = typeof ttlSeconds === "number" ? ttlSeconds * 1e3 : void 0;
@@ -12009,7 +12246,9 @@ var InMemoryConversationStore = class {
12009
12246
  }
12010
12247
  async get(conversationId) {
12011
12248
  this.purgeExpired();
12012
- return this.conversations.get(conversationId);
12249
+ const c = this.conversations.get(conversationId);
12250
+ if (!c) return void 0;
12251
+ return rebuildConversationFromEntries({ ...c }, (id) => this.readEntries(id));
12013
12252
  }
12014
12253
  // In-memory stores already hold the full conversation object, so there's
12015
12254
  // no separate archive blob to load. Both variants return the same data.
@@ -12102,6 +12341,30 @@ var InMemoryConversationStore = class {
12102
12341
  channelMeta: c.channelMeta
12103
12342
  }));
12104
12343
  }
12344
+ async appendEntries(conversationId, _agentId, _tenantId, entries) {
12345
+ const list = this.entries.get(conversationId) ?? [];
12346
+ let nextSeq = list.reduce((max, e) => e.seq > max ? e.seq : max, 0) + 1;
12347
+ const now2 = Date.now();
12348
+ const stored = entries.map(
12349
+ (e) => ({ ...e, seq: nextSeq++, createdAt: now2 })
12350
+ );
12351
+ this.entries.set(conversationId, [...list, ...stored]);
12352
+ return stored;
12353
+ }
12354
+ async readEntries(conversationId, opts) {
12355
+ let list = (this.entries.get(conversationId) ?? []).slice().sort((a, b) => a.seq - b.seq);
12356
+ if (opts?.types && opts.types.length > 0) {
12357
+ const allowed = new Set(opts.types);
12358
+ list = list.filter((e) => allowed.has(e.type));
12359
+ }
12360
+ if (typeof opts?.afterSeq === "number") {
12361
+ list = list.filter((e) => e.seq > opts.afterSeq);
12362
+ }
12363
+ if (typeof opts?.limit === "number") {
12364
+ list = list.slice(0, opts.limit);
12365
+ }
12366
+ return list;
12367
+ }
12105
12368
  };
12106
12369
  var createStateStore = (config, _options) => {
12107
12370
  const ttl = config?.ttl;
@@ -12444,9 +12707,143 @@ var CALLBACK_LOCK_STALE_MS = 5 * 60 * 1e3;
12444
12707
  var STALE_SUBAGENT_THRESHOLD_MS = 5 * 60 * 1e3;
12445
12708
 
12446
12709
  // src/orchestrator/orchestrator.ts
12710
+ import { createLogger as createLogger8, getTextContent as getTextContent4 } from "@poncho-ai/sdk";
12711
+
12712
+ // src/orchestrator/entries-dual-write.ts
12713
+ import { randomUUID as randomUUID6 } from "crypto";
12447
12714
  import { getTextContent as getTextContent3 } from "@poncho-ai/sdk";
12715
+ var entriesParityEnabled = () => process.env.PONCHO_VERIFY_ENTRIES === "1";
12716
+ var appendEntriesSafe = async (store, conversation, entries, log2) => {
12717
+ if (entries.length === 0) return [];
12718
+ try {
12719
+ const withIds = entries.map(
12720
+ (e) => ({ id: randomUUID6(), ...e })
12721
+ );
12722
+ return await store.appendEntries(
12723
+ conversation.conversationId,
12724
+ conversation.ownerId,
12725
+ conversation.tenantId ?? null,
12726
+ withIds
12727
+ );
12728
+ } catch (err) {
12729
+ log2.error(
12730
+ `[entries-dual-write] append failed for ${conversation.conversationId}: ${err instanceof Error ? err.message : String(err)}`
12731
+ );
12732
+ return [];
12733
+ }
12734
+ };
12735
+ var userMessageEntry = (message, turnId, opts) => ({
12736
+ type: "user_message",
12737
+ message,
12738
+ turnId,
12739
+ ...opts?.hidden ? { hidden: true } : {}
12740
+ });
12741
+ var assistantMessageEntry = (message, turnId, runId) => ({
12742
+ type: "assistant_message",
12743
+ message,
12744
+ turnId,
12745
+ runId
12746
+ });
12747
+ var harnessMessageEntries = (messages, turnId) => messages.map((message) => ({ type: "harness_message", message, turnId }));
12748
+ var compactionEntry = (summaryMessage, firstKeptSeq, opts) => ({
12749
+ type: "compaction",
12750
+ summaryMessage,
12751
+ firstKeptSeq,
12752
+ ...opts?.tokensBefore !== void 0 ? { tokensBefore: opts.tokensBefore } : {},
12753
+ ...opts?.tokensAfter !== void 0 ? { tokensAfter: opts.tokensAfter } : {}
12754
+ });
12755
+ var subagentResultEntry = (result) => ({ type: "subagent_result", result });
12756
+ var callbackStartedEntry = (consumedSeqs) => ({
12757
+ type: "callback_started",
12758
+ consumedSeqs
12759
+ });
12760
+ var assistantAmendmentEntry = (targetEntryId, appendText) => ({
12761
+ type: "assistant_amendment",
12762
+ targetEntryId,
12763
+ ...appendText ? { appendText } : {}
12764
+ });
12765
+ var newHarnessMessagesThisTurn = (prev, next) => {
12766
+ const prevArr = prev ?? [];
12767
+ const nextArr = next ?? [];
12768
+ if (nextArr.length === 0) return { messages: [], approximate: false };
12769
+ if (prevArr.length === 0) return { messages: nextArr, approximate: false };
12770
+ if (nextArr.length >= prevArr.length) {
12771
+ return { messages: nextArr.slice(prevArr.length), approximate: false };
12772
+ }
12773
+ return { messages: nextArr, approximate: true };
12774
+ };
12775
+ var projectText = (m) => {
12776
+ const role = m.role;
12777
+ const text = getTextContent3(m).replace(/\s+/g, " ").trim();
12778
+ return `${role}:${text}`;
12779
+ };
12780
+ var projectAll = (msgs) => msgs.map(projectText);
12781
+ var countMismatch = (label, a, b) => a === b ? null : `${label} length ${a} (entries) vs ${b} (blob)`;
12782
+ var verifyEntriesParity = async (store, conversationId, blob, log2) => {
12783
+ if (!entriesParityEnabled()) return;
12784
+ try {
12785
+ const entries = await store.readEntries(conversationId);
12786
+ const mismatches = [];
12787
+ if (blob.harnessMessages) {
12788
+ const llm = buildLlmContext(entries);
12789
+ const lenMismatch = countMismatch(
12790
+ "llmContext",
12791
+ llm.length,
12792
+ blob.harnessMessages.length
12793
+ );
12794
+ if (lenMismatch) mismatches.push(lenMismatch);
12795
+ const entriesProj = projectAll(llm);
12796
+ const blobProj = projectAll(blob.harnessMessages);
12797
+ const tail = Math.min(entriesProj.length, blobProj.length, 5);
12798
+ for (let i = 1; i <= tail; i++) {
12799
+ const ep = entriesProj[entriesProj.length - i];
12800
+ const bp = blobProj[blobProj.length - i];
12801
+ if (ep !== bp) {
12802
+ mismatches.push(
12803
+ `llmContext tail[-${i}] differs: entries=${JSON.stringify(ep).slice(0, 120)} blob=${JSON.stringify(bp).slice(0, 120)}`
12804
+ );
12805
+ }
12806
+ }
12807
+ }
12808
+ if (blob.displayMessages) {
12809
+ const snap = buildDisplaySnapshot(entries, Number.MAX_SAFE_INTEGER);
12810
+ const lenMismatch = countMismatch(
12811
+ "display",
12812
+ snap.totalMessages,
12813
+ blob.displayMessages.length
12814
+ );
12815
+ if (lenMismatch) mismatches.push(lenMismatch);
12816
+ const entriesProj = projectAll(snap.messages);
12817
+ const blobProj = projectAll(blob.displayMessages);
12818
+ const tail = Math.min(entriesProj.length, blobProj.length, 5);
12819
+ for (let i = 1; i <= tail; i++) {
12820
+ const ep = entriesProj[entriesProj.length - i];
12821
+ const bp = blobProj[blobProj.length - i];
12822
+ if (ep !== bp) {
12823
+ mismatches.push(
12824
+ `display tail[-${i}] differs: entries=${JSON.stringify(ep).slice(0, 120)} blob=${JSON.stringify(bp).slice(0, 120)}`
12825
+ );
12826
+ }
12827
+ }
12828
+ }
12829
+ if (mismatches.length > 0) {
12830
+ log2.warn(
12831
+ `[entries-parity] ${conversationId} MISMATCH (${mismatches.length}): ${mismatches.join(" | ")}`
12832
+ );
12833
+ } else {
12834
+ log2.info(`[entries-parity] ${conversationId} OK`);
12835
+ }
12836
+ } catch (err) {
12837
+ log2.error(
12838
+ `[entries-parity] ${conversationId} checker threw (ignored): ${err instanceof Error ? err.message : String(err)}`
12839
+ );
12840
+ }
12841
+ };
12842
+
12843
+ // src/orchestrator/orchestrator.ts
12844
+ var dualWriteLog = createLogger8("orchestrator:entries");
12448
12845
  var assistantMessageText = (message) => {
12449
- const raw = getTextContent3(message).trim();
12846
+ const raw = getTextContent4(message).trim();
12450
12847
  if (raw.startsWith("{") && raw.includes('"tool_calls"')) {
12451
12848
  try {
12452
12849
  const parsed = JSON.parse(raw);
@@ -12755,6 +13152,8 @@ var AgentOrchestrator = class {
12755
13152
  if (!checkpointedRun) {
12756
13153
  const conv = await this.conversationStore.get(conversationId);
12757
13154
  if (conv) {
13155
+ let amendmentText;
13156
+ let pushedAssistant;
12758
13157
  const hasAssistantContent = draft.assistantResponse.length > 0 || draft.toolTimeline.length > 0 || draft.sections.length > 0;
12759
13158
  if (hasAssistantContent) {
12760
13159
  const prevMessages = conv.messages;
@@ -12782,15 +13181,14 @@ var AgentOrchestrator = class {
12782
13181
  }
12783
13182
  }
12784
13183
  ];
13184
+ amendmentText = draft.assistantResponse;
12785
13185
  } else {
12786
- conv.messages = [
12787
- ...prevMessages,
12788
- {
12789
- role: "assistant",
12790
- content: draft.assistantResponse,
12791
- metadata: buildAssistantMetadata(draft)
12792
- }
12793
- ];
13186
+ pushedAssistant = {
13187
+ role: "assistant",
13188
+ content: draft.assistantResponse,
13189
+ metadata: buildAssistantMetadata(draft)
13190
+ };
13191
+ conv.messages = [...prevMessages, pushedAssistant];
12794
13192
  }
12795
13193
  }
12796
13194
  applyTurnMetadata(conv, {
@@ -12800,6 +13198,54 @@ var AgentOrchestrator = class {
12800
13198
  harnessMessages: execution?.runHarnessMessages
12801
13199
  }, { shouldRebuildCanonical: true });
12802
13200
  await this.conversationStore.update(conv);
13201
+ if (amendmentText !== void 0 || pushedAssistant) {
13202
+ const finalConv = conv;
13203
+ const amendText = amendmentText;
13204
+ const pushed = pushedAssistant;
13205
+ void (async () => {
13206
+ try {
13207
+ if (pushed) {
13208
+ await appendEntriesSafe(
13209
+ this.conversationStore,
13210
+ finalConv,
13211
+ [assistantMessageEntry(pushed, `resume-${conversationId}`, latestRunId)],
13212
+ dualWriteLog
13213
+ );
13214
+ } else if (amendText !== void 0) {
13215
+ const existing = await this.conversationStore.readEntries(
13216
+ conversationId,
13217
+ { types: ["assistant_message"] }
13218
+ );
13219
+ const target = existing[existing.length - 1];
13220
+ if (target) {
13221
+ await appendEntriesSafe(
13222
+ this.conversationStore,
13223
+ finalConv,
13224
+ [assistantAmendmentEntry(target.id, amendText)],
13225
+ dualWriteLog
13226
+ );
13227
+ } else {
13228
+ dualWriteLog.warn(
13229
+ `[entries-dual-write] resume amendment for ${conversationId}: no assistant_message entry to target; skipped`
13230
+ );
13231
+ }
13232
+ }
13233
+ await verifyEntriesParity(
13234
+ this.conversationStore,
13235
+ conversationId,
13236
+ {
13237
+ harnessMessages: finalConv._harnessMessages,
13238
+ displayMessages: finalConv.messages
13239
+ },
13240
+ dualWriteLog
13241
+ );
13242
+ } catch (err) {
13243
+ dualWriteLog.error(
13244
+ `[entries-dual-write] resume finalize append failed for ${conversationId}: ${err instanceof Error ? err.message : String(err)}`
13245
+ );
13246
+ }
13247
+ })();
13248
+ }
12803
13249
  }
12804
13250
  } else {
12805
13251
  const conv = await this.conversationStore.get(conversationId);
@@ -13310,19 +13756,22 @@ var AgentOrchestrator = class {
13310
13756
  conversation.runStatus = "running";
13311
13757
  const callbackCount = (conversation.subagentCallbackCount ?? 0) + 1;
13312
13758
  conversation.subagentCallbackCount = callbackCount;
13759
+ const injectedCallbackMessages = [];
13313
13760
  for (const pr of pendingResults) {
13314
13761
  const responseText = (pr.result?.response ?? "").trim();
13315
13762
  const responseLine = responseText || `(subagent produced no final summary after ${pr.result?.steps ?? 0} step(s); its work may be incomplete. Call read_subagent with subagent_id "${pr.subagentId}" and mode "assistant" to retrieve what it did.)`;
13316
13763
  const resultBody = pr.result ? `Status: ${pr.result.status}
13317
13764
  Response: ${responseLine}
13318
13765
  Steps: ${pr.result.steps}, Duration: ${pr.result.duration}ms` : pr.error ? `Error: ${pr.error.message}` : "(no result)";
13319
- conversation.messages.push({
13766
+ const injected = {
13320
13767
  role: "user",
13321
13768
  content: `[Subagent Result] Subagent "${pr.task}" (${pr.subagentId}) ${pr.status}:
13322
13769
 
13323
13770
  ${resultBody}`,
13324
13771
  metadata: { _subagentCallback: true, subagentId: pr.subagentId, task: pr.task, timestamp: pr.timestamp }
13325
- });
13772
+ };
13773
+ injectedCallbackMessages.push(injected);
13774
+ conversation.messages.push(injected);
13326
13775
  }
13327
13776
  const processedIds = new Set(pendingResults.map((pr) => pr.subagentId));
13328
13777
  const freshForPending = await this.conversationStore.get(conversationId);
@@ -13331,6 +13780,36 @@ ${resultBody}`,
13331
13780
  conversation._harnessMessages = [...conversation.messages];
13332
13781
  conversation.updatedAt = Date.now();
13333
13782
  await this.conversationStore.update(conversation);
13783
+ if (pendingResults.length > 0) {
13784
+ const turnId = `callback-${callbackCount}-${conversation.conversationId}`;
13785
+ void (async () => {
13786
+ try {
13787
+ const resultEntries = await this.conversationStore.readEntries(
13788
+ conversation.conversationId,
13789
+ { types: ["subagent_result"] }
13790
+ );
13791
+ const consumedIds = new Set(pendingResults.map((pr) => pr.subagentId));
13792
+ const consumedSeqs = resultEntries.filter(
13793
+ (e) => e.type === "subagent_result" && consumedIds.has(e.result.subagentId)
13794
+ ).map((e) => e.seq);
13795
+ await appendEntriesSafe(
13796
+ this.conversationStore,
13797
+ conversation,
13798
+ [
13799
+ callbackStartedEntry(consumedSeqs),
13800
+ ...injectedCallbackMessages.map(
13801
+ (m) => userMessageEntry(m, turnId, { hidden: true })
13802
+ )
13803
+ ],
13804
+ dualWriteLog
13805
+ );
13806
+ } catch (err) {
13807
+ dualWriteLog.error(
13808
+ `[entries-dual-write] callback_started append failed for ${conversation.conversationId}: ${err instanceof Error ? err.message : String(err)}`
13809
+ );
13810
+ }
13811
+ })();
13812
+ }
13334
13813
  if (callbackCount > MAX_SUBAGENT_CALLBACK_COUNT) {
13335
13814
  console.warn(`[poncho][subagent-callback] Circuit breaker: ${callbackCount} callbacks for ${conversationId}, skipping re-run`);
13336
13815
  conversation.runningCallbackSince = void 0;
@@ -13392,12 +13871,14 @@ ${resultBody}`,
13392
13871
  if (callbackNeedsContinuation || execution.draft.assistantResponse.length > 0 || execution.draft.toolTimeline.length > 0) {
13393
13872
  const freshConv = await this.conversationStore.get(conversationId);
13394
13873
  if (freshConv) {
13874
+ let callbackAssistantMsg;
13395
13875
  if (!callbackNeedsContinuation) {
13396
- freshConv.messages.push({
13876
+ callbackAssistantMsg = {
13397
13877
  role: "assistant",
13398
13878
  content: execution.draft.assistantResponse,
13399
13879
  metadata: buildAssistantMetadata(execution.draft)
13400
- });
13880
+ };
13881
+ freshConv.messages.push(callbackAssistantMsg);
13401
13882
  }
13402
13883
  applyTurnMetadata(freshConv, {
13403
13884
  latestRunId: execution.latestRunId,
@@ -13410,6 +13891,25 @@ ${resultBody}`,
13410
13891
  }, { shouldRebuildCanonical: true, clearApprovals: false });
13411
13892
  freshConv.runningCallbackSince = void 0;
13412
13893
  await this.conversationStore.update(freshConv);
13894
+ if (callbackAssistantMsg) {
13895
+ const finalMsg = callbackAssistantMsg;
13896
+ void appendEntriesSafe(
13897
+ this.conversationStore,
13898
+ freshConv,
13899
+ [assistantMessageEntry(finalMsg, `callback-${conversationId}`, execution.latestRunId)],
13900
+ dualWriteLog
13901
+ ).then(
13902
+ () => verifyEntriesParity(
13903
+ this.conversationStore,
13904
+ conversationId,
13905
+ {
13906
+ harnessMessages: freshConv._harnessMessages,
13907
+ displayMessages: freshConv.messages
13908
+ },
13909
+ dualWriteLog
13910
+ )
13911
+ );
13912
+ }
13413
13913
  if (freshConv.channelMeta && execution.draft.assistantResponse.length > 0) {
13414
13914
  this.hooks?.onMessagingNotify?.(conversationId, execution.draft.assistantResponse);
13415
13915
  }
@@ -13814,6 +14314,22 @@ ${resultBody}`,
13814
14314
  * old `.catch(() => {})` call sites did. Returns whether the result landed.
13815
14315
  */
13816
14316
  async appendSubagentResultReliable(parentConversationId, result) {
14317
+ void (async () => {
14318
+ try {
14319
+ const parent = await this.conversationStore.get(parentConversationId);
14320
+ if (!parent) return;
14321
+ await appendEntriesSafe(
14322
+ this.conversationStore,
14323
+ parent,
14324
+ [subagentResultEntry(result)],
14325
+ dualWriteLog
14326
+ );
14327
+ } catch (err) {
14328
+ dualWriteLog.error(
14329
+ `[entries-dual-write] subagent_result append failed for ${parentConversationId}: ${err instanceof Error ? err.message : String(err)}`
14330
+ );
14331
+ }
14332
+ })();
13817
14333
  try {
13818
14334
  await this.conversationStore.appendSubagentResult(parentConversationId, result);
13819
14335
  return true;
@@ -13915,9 +14431,9 @@ ${resultBody}`,
13915
14431
  };
13916
14432
 
13917
14433
  // src/orchestrator/run-conversation-turn.ts
13918
- import { randomUUID as randomUUID6 } from "crypto";
13919
- import { createLogger as createLogger7 } from "@poncho-ai/sdk";
13920
- var log = createLogger7("orchestrator");
14434
+ import { randomUUID as randomUUID7 } from "crypto";
14435
+ import { createLogger as createLogger9 } from "@poncho-ai/sdk";
14436
+ var log = createLogger9("orchestrator");
13921
14437
  var runConversationTurn = async (opts) => {
13922
14438
  const conversation = await opts.conversationStore.getWithArchive(opts.conversationId);
13923
14439
  if (!conversation) {
@@ -13955,9 +14471,9 @@ var runConversationTurn = async (opts) => {
13955
14471
  const userMessage = {
13956
14472
  role: "user",
13957
14473
  content: userContent,
13958
- metadata: { id: randomUUID6(), timestamp: turnTimestamp }
14474
+ metadata: { id: randomUUID7(), timestamp: turnTimestamp }
13959
14475
  };
13960
- const assistantId = randomUUID6();
14476
+ const assistantId = randomUUID7();
13961
14477
  const draft = createTurnDraftState();
13962
14478
  let latestRunId = conversation.runtimeRunId ?? "";
13963
14479
  let runCancelled = false;
@@ -13996,6 +14512,8 @@ var runConversationTurn = async (opts) => {
13996
14512
  conversation.updatedAt = Date.now();
13997
14513
  await opts.conversationStore.update(conversation);
13998
14514
  };
14515
+ const preTurnHarnessMessages = conversation._harnessMessages ? [...conversation._harnessMessages] : void 0;
14516
+ const turnId = assistantId;
13999
14517
  conversation.messages = [...historyMessages, userMessage];
14000
14518
  conversation.subagentCallbackCount = 0;
14001
14519
  conversation._continuationCount = void 0;
@@ -14005,6 +14523,12 @@ var runConversationTurn = async (opts) => {
14005
14523
  `failed to persist user turn: ${err instanceof Error ? err.message : String(err)}`
14006
14524
  );
14007
14525
  });
14526
+ void appendEntriesSafe(
14527
+ opts.conversationStore,
14528
+ conversation,
14529
+ [userMessageEntry(userMessage, turnId)],
14530
+ log
14531
+ );
14008
14532
  try {
14009
14533
  const execution = await executeConversationTurn({
14010
14534
  harness: opts.harness,
@@ -14052,6 +14576,34 @@ var runConversationTurn = async (opts) => {
14052
14576
  ...existingHistory,
14053
14577
  ...preRunMessages.slice(0, removedCount)
14054
14578
  ];
14579
+ const summaryMessage = event.compactedMessages[0];
14580
+ const keptCount = Math.max(0, event.compactedMessages.length - 1);
14581
+ if (summaryMessage) {
14582
+ void (async () => {
14583
+ try {
14584
+ const existing = await opts.conversationStore.readEntries(
14585
+ opts.conversationId,
14586
+ { types: ["harness_message"] }
14587
+ );
14588
+ const harnessSeqs = existing.map((e) => e.seq);
14589
+ const firstKeptSeq = harnessSeqs.length >= keptCount && keptCount > 0 ? harnessSeqs[harnessSeqs.length - keptCount] : (harnessSeqs[harnessSeqs.length - 1] ?? 0) + 1;
14590
+ await appendEntriesSafe(
14591
+ opts.conversationStore,
14592
+ conversation,
14593
+ [
14594
+ compactionEntry(summaryMessage, firstKeptSeq, {
14595
+ tokensBefore: conversation.contextTokens
14596
+ })
14597
+ ],
14598
+ log
14599
+ );
14600
+ } catch (err) {
14601
+ log.error(
14602
+ `[entries-dual-write] compaction append failed: ${err instanceof Error ? err.message : String(err)}`
14603
+ );
14604
+ }
14605
+ })();
14606
+ }
14055
14607
  }
14056
14608
  }
14057
14609
  if (event.type === "step:completed") {
@@ -14185,6 +14737,36 @@ var runConversationTurn = async (opts) => {
14185
14737
  { shouldRebuildCanonical }
14186
14738
  );
14187
14739
  await opts.conversationStore.update(conversation);
14740
+ const finalAssistant = conversation.messages[conversation.messages.length - 1];
14741
+ const { messages: newHarness, approximate } = newHarnessMessagesThisTurn(
14742
+ preTurnHarnessMessages,
14743
+ conversation._harnessMessages
14744
+ );
14745
+ if (approximate) {
14746
+ log.warn(
14747
+ `[entries-dual-write] ${opts.conversationId} harness-message diff approximate (blob array shrank this turn \u2014 likely compaction); appended full context`
14748
+ );
14749
+ }
14750
+ const finalizeEntries = [
14751
+ ...harnessMessageEntries(newHarness, turnId),
14752
+ ...finalAssistant && finalAssistant.role === "assistant" ? [assistantMessageEntry(finalAssistant, turnId, latestRunId)] : []
14753
+ ];
14754
+ void appendEntriesSafe(
14755
+ opts.conversationStore,
14756
+ conversation,
14757
+ finalizeEntries,
14758
+ log
14759
+ ).then(
14760
+ () => verifyEntriesParity(
14761
+ opts.conversationStore,
14762
+ opts.conversationId,
14763
+ {
14764
+ harnessMessages: conversation._harnessMessages,
14765
+ displayMessages: conversation.messages
14766
+ },
14767
+ log
14768
+ )
14769
+ );
14188
14770
  }
14189
14771
  return {
14190
14772
  latestRunId,
@@ -14302,10 +14884,13 @@ export {
14302
14884
  VFS_SCHEME,
14303
14885
  VercelBlobUploadStore,
14304
14886
  abnormalEndResponse,
14887
+ appendEntriesSafe,
14305
14888
  applyTurnMetadata,
14306
14889
  buildAgentDirectoryName,
14307
14890
  buildApprovalCheckpoints,
14308
14891
  buildAssistantMetadata,
14892
+ buildDisplaySnapshot,
14893
+ buildLlmContext,
14309
14894
  buildSkillContextWindow,
14310
14895
  buildToolCompletedText,
14311
14896
  cloneSections,
@@ -14342,6 +14927,7 @@ export {
14342
14927
  deleteOpenAICodexSession,
14343
14928
  deriveUploadKey,
14344
14929
  ensureAgentIdentity,
14930
+ entriesParityEnabled,
14345
14931
  estimateTokens,
14346
14932
  estimateTotalTokens,
14347
14933
  executeConversationTurn,
@@ -14353,6 +14939,7 @@ export {
14353
14939
  getOpenAICodexAccessToken,
14354
14940
  getOpenAICodexAuthFilePath,
14355
14941
  getOpenAICodexRequiredScopes,
14942
+ getPendingSubagentResults,
14356
14943
  getPonchoStoreRoot,
14357
14944
  isMessageArray,
14358
14945
  jsonSchemaToZod,
@@ -14366,6 +14953,7 @@ export {
14366
14953
  loadSkillMetadataFromDirs,
14367
14954
  loadVfsSkillMetadata,
14368
14955
  mergeSkills,
14956
+ newHarnessMessagesThisTurn,
14369
14957
  normalizeApprovalCheckpoint,
14370
14958
  normalizeOtlp,
14371
14959
  normalizeScriptPolicyPath,
@@ -14389,6 +14977,7 @@ export {
14389
14977
  runConversationTurn,
14390
14978
  slugifyStorageComponent,
14391
14979
  startOpenAICodexDeviceAuth,
14980
+ verifyEntriesParity,
14392
14981
  verifyTenantToken,
14393
14982
  withToolResultArchiveParam,
14394
14983
  writeOpenAICodexSession