@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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +54 -0
- package/dist/index.d.ts +210 -2
- package/dist/index.js +632 -43
- package/package.json +1 -1
- package/src/index.ts +18 -0
- package/src/orchestrator/entries-dual-write.ts +265 -0
- package/src/orchestrator/index.ts +7 -0
- package/src/orchestrator/orchestrator.ts +179 -13
- package/src/orchestrator/run-conversation-turn.ts +108 -0
- package/src/state.ts +66 -1
- package/src/storage/engine.ts +18 -0
- package/src/storage/entries.ts +70 -2
- package/src/storage/memory-engine.ts +57 -2
- package/src/storage/schema.ts +30 -0
- package/src/storage/sql-dialect.ts +127 -2
- package/src/storage/store-adapters.ts +8 -0
- package/test/entries-dual-write.test.ts +172 -0
- package/test/entries-read-cutover.test.ts +96 -0
- package/test/entries-store.test.ts +165 -0
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
6445
|
-
var mcpLog =
|
|
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
|
|
7439
|
-
var logger =
|
|
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
|
|
8556
|
-
var eventLog =
|
|
8557
|
-
var telemetryLog =
|
|
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 =
|
|
8729
|
-
var telemetryLog2 =
|
|
8730
|
-
var costLog =
|
|
8731
|
-
var mcpLog2 =
|
|
8732
|
-
var modelLog =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
13919
|
-
import { createLogger as
|
|
13920
|
-
var log =
|
|
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:
|
|
14474
|
+
metadata: { id: randomUUID7(), timestamp: turnTimestamp }
|
|
13959
14475
|
};
|
|
13960
|
-
const assistantId =
|
|
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
|