openmates 0.15.0-alpha.73 → 0.15.0-alpha.75
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/{chunk-VJLMFEX6.js → chunk-U4XICRVH.js} +744 -97
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +20 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -1356,6 +1356,9 @@ function websocketProtocolError(envelope) {
|
|
|
1356
1356
|
}
|
|
1357
1357
|
return null;
|
|
1358
1358
|
}
|
|
1359
|
+
function numberMetric(value) {
|
|
1360
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1361
|
+
}
|
|
1359
1362
|
function errorFrameBelongsToAiResponse(envelope, userMessageId, chatId, recoveryTurnId) {
|
|
1360
1363
|
if (envelope.type === "client_update_required") return true;
|
|
1361
1364
|
if (envelope.type !== "error") return true;
|
|
@@ -1722,6 +1725,8 @@ var OpenMatesWsClient = class {
|
|
|
1722
1725
|
let taskId = null;
|
|
1723
1726
|
let category = null;
|
|
1724
1727
|
let modelName = null;
|
|
1728
|
+
let tokenUsage = null;
|
|
1729
|
+
let promptBudget = null;
|
|
1725
1730
|
let recoveryJobId = null;
|
|
1726
1731
|
const compressionCheckpoints = [];
|
|
1727
1732
|
let followUpSuggestions = [];
|
|
@@ -1767,6 +1772,24 @@ var OpenMatesWsClient = class {
|
|
|
1767
1772
|
if (typeof p.category === "string" && p.category) category = p.category;
|
|
1768
1773
|
if (typeof p.model_name === "string" && p.model_name)
|
|
1769
1774
|
modelName = p.model_name;
|
|
1775
|
+
const promptTokens = numberMetric(p.prompt_tokens);
|
|
1776
|
+
const completionTokens = numberMetric(p.completion_tokens);
|
|
1777
|
+
const userInputTokens = numberMetric(p.user_input_tokens);
|
|
1778
|
+
const systemPromptTokens = numberMetric(p.system_prompt_tokens);
|
|
1779
|
+
const totalCredits = numberMetric(p.total_credits);
|
|
1780
|
+
const hasTokenUsage = promptTokens !== null || completionTokens !== null || userInputTokens !== null || systemPromptTokens !== null || totalCredits !== null;
|
|
1781
|
+
if (hasTokenUsage) {
|
|
1782
|
+
tokenUsage = {
|
|
1783
|
+
...promptTokens !== null ? { promptTokens } : {},
|
|
1784
|
+
...completionTokens !== null ? { completionTokens } : {},
|
|
1785
|
+
...userInputTokens !== null ? { userInputTokens } : {},
|
|
1786
|
+
...systemPromptTokens !== null ? { systemPromptTokens } : {},
|
|
1787
|
+
...totalCredits !== null ? { totalCredits } : {}
|
|
1788
|
+
};
|
|
1789
|
+
}
|
|
1790
|
+
if (systemPromptTokens !== null) {
|
|
1791
|
+
promptBudget = { systemPromptTokens };
|
|
1792
|
+
}
|
|
1770
1793
|
if (typeof p.recovery_job_id === "string" && p.recovery_job_id && p.recovery_protocol_version === 1)
|
|
1771
1794
|
recoveryJobId = p.recovery_job_id;
|
|
1772
1795
|
};
|
|
@@ -1809,7 +1832,9 @@ var OpenMatesWsClient = class {
|
|
|
1809
1832
|
embeds: [...embeds.values()],
|
|
1810
1833
|
subChatEvents,
|
|
1811
1834
|
recoveryJobId,
|
|
1812
|
-
compressionCheckpoints
|
|
1835
|
+
compressionCheckpoints,
|
|
1836
|
+
tokenUsage,
|
|
1837
|
+
promptBudget
|
|
1813
1838
|
});
|
|
1814
1839
|
return;
|
|
1815
1840
|
}
|
|
@@ -1839,7 +1864,9 @@ var OpenMatesWsClient = class {
|
|
|
1839
1864
|
embeds: [...embeds.values()],
|
|
1840
1865
|
subChatEvents,
|
|
1841
1866
|
recoveryJobId,
|
|
1842
|
-
compressionCheckpoints
|
|
1867
|
+
compressionCheckpoints,
|
|
1868
|
+
tokenUsage,
|
|
1869
|
+
promptBudget
|
|
1843
1870
|
});
|
|
1844
1871
|
}, asyncEmbedWaitMs);
|
|
1845
1872
|
return;
|
|
@@ -1865,7 +1892,9 @@ var OpenMatesWsClient = class {
|
|
|
1865
1892
|
embeds: [...embeds.values()],
|
|
1866
1893
|
subChatEvents,
|
|
1867
1894
|
recoveryJobId,
|
|
1868
|
-
compressionCheckpoints
|
|
1895
|
+
compressionCheckpoints,
|
|
1896
|
+
tokenUsage,
|
|
1897
|
+
promptBudget
|
|
1869
1898
|
});
|
|
1870
1899
|
};
|
|
1871
1900
|
const handleSubChatEvent = (type, p) => {
|
|
@@ -2139,7 +2168,9 @@ var OpenMatesWsClient = class {
|
|
|
2139
2168
|
embeds: [...embeds.values()],
|
|
2140
2169
|
subChatEvents,
|
|
2141
2170
|
recoveryJobId,
|
|
2142
|
-
compressionCheckpoints
|
|
2171
|
+
compressionCheckpoints,
|
|
2172
|
+
tokenUsage,
|
|
2173
|
+
promptBudget
|
|
2143
2174
|
});
|
|
2144
2175
|
return;
|
|
2145
2176
|
}
|
|
@@ -6388,7 +6419,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
6388
6419
|
taskProposals: [],
|
|
6389
6420
|
taskUpdateProposals: [],
|
|
6390
6421
|
subChatEvents: [],
|
|
6391
|
-
appSettingsMemoryRequests: []
|
|
6422
|
+
appSettingsMemoryRequests: [],
|
|
6423
|
+
tokenUsage: null,
|
|
6424
|
+
promptBudget: null
|
|
6392
6425
|
};
|
|
6393
6426
|
}
|
|
6394
6427
|
async createTurnTokenRefs(params) {
|
|
@@ -6574,7 +6607,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
6574
6607
|
if (!userId) {
|
|
6575
6608
|
throw new Error("Could not resolve current user id for local connected-account connector.");
|
|
6576
6609
|
}
|
|
6577
|
-
const { createHash:
|
|
6610
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
6578
6611
|
const accountId = randomUUID5();
|
|
6579
6612
|
const masterKey = this.getMasterKeyBytes();
|
|
6580
6613
|
const encryptLocalConnectorValue = async (value) => {
|
|
@@ -6583,9 +6616,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
6583
6616
|
};
|
|
6584
6617
|
const payload = {
|
|
6585
6618
|
id: accountId,
|
|
6586
|
-
hashed_user_id:
|
|
6619
|
+
hashed_user_id: createHash15("sha256").update(userId).digest("hex"),
|
|
6587
6620
|
encrypted_provider_type: await encryptLocalConnectorValue(input.provider_id),
|
|
6588
|
-
provider_type_hash:
|
|
6621
|
+
provider_type_hash: createHash15("sha256").update(input.provider_id).digest("hex"),
|
|
6589
6622
|
encrypted_account_label: await encryptLocalConnectorValue(input.label),
|
|
6590
6623
|
encrypted_capabilities: await encryptLocalConnectorValue(input.capabilities),
|
|
6591
6624
|
encrypted_app_permissions: await encryptLocalConnectorValue({
|
|
@@ -7970,8 +8003,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
7970
8003
|
);
|
|
7971
8004
|
}
|
|
7972
8005
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
7973
|
-
const { createHash:
|
|
7974
|
-
const hashedEmbedId =
|
|
8006
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
8007
|
+
const hashedEmbedId = createHash15("sha256").update(embedId).digest("hex");
|
|
7975
8008
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
7976
8009
|
cache,
|
|
7977
8010
|
masterKey,
|
|
@@ -8079,7 +8112,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8079
8112
|
async resolveEmbedKey(cache, masterKey, embed, embedId, hashedEmbedId, visited = /* @__PURE__ */ new Set()) {
|
|
8080
8113
|
if (visited.has(embedId)) return null;
|
|
8081
8114
|
visited.add(embedId);
|
|
8082
|
-
const { createHash:
|
|
8115
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
8083
8116
|
const masterKeyEntry = cache.embedKeys.find(
|
|
8084
8117
|
(ek) => ek.hashed_embed_id === hashedEmbedId && String(ek.key_type) === "master"
|
|
8085
8118
|
);
|
|
@@ -8096,7 +8129,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8096
8129
|
if (chatKeyEntry && typeof chatKeyEntry.encrypted_embed_key === "string") {
|
|
8097
8130
|
const hashedChatId = String(chatKeyEntry.hashed_chat_id ?? "");
|
|
8098
8131
|
const owningChat = cache.chats.find((c) => {
|
|
8099
|
-
const chatHash =
|
|
8132
|
+
const chatHash = createHash15("sha256").update(String(c.details.id ?? "")).digest("hex");
|
|
8100
8133
|
return chatHash === hashedChatId;
|
|
8101
8134
|
});
|
|
8102
8135
|
if (owningChat) {
|
|
@@ -8125,7 +8158,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8125
8158
|
const parentFullId = String(
|
|
8126
8159
|
parentEmbed2.embed_id ?? parentEmbed2.id ?? ""
|
|
8127
8160
|
);
|
|
8128
|
-
const parentHashedId =
|
|
8161
|
+
const parentHashedId = createHash15("sha256").update(parentFullId).digest("hex");
|
|
8129
8162
|
const parentKey = await this.resolveEmbedKey(
|
|
8130
8163
|
cache,
|
|
8131
8164
|
masterKey,
|
|
@@ -8555,6 +8588,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8555
8588
|
let assistantMessageId = null;
|
|
8556
8589
|
let category = null;
|
|
8557
8590
|
let modelName = null;
|
|
8591
|
+
let tokenUsage = null;
|
|
8592
|
+
let promptBudget = null;
|
|
8558
8593
|
let followUpSuggestions = [];
|
|
8559
8594
|
let taskProposals = [];
|
|
8560
8595
|
let taskUpdateProposals = [];
|
|
@@ -8786,6 +8821,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8786
8821
|
assistant = resp.content;
|
|
8787
8822
|
category = resp.category;
|
|
8788
8823
|
modelName = resp.modelName;
|
|
8824
|
+
tokenUsage = resp.tokenUsage;
|
|
8825
|
+
promptBudget = resp.promptBudget;
|
|
8789
8826
|
taskEvents = resp.taskEvents;
|
|
8790
8827
|
pendingTaskUpdateJobs = taskUpdateJobsEnabled ? resp.pendingTaskUpdateJobs : [];
|
|
8791
8828
|
subChatEvents = resp.subChatEvents;
|
|
@@ -8805,7 +8842,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8805
8842
|
taskEvents,
|
|
8806
8843
|
pendingTaskUpdateJobs,
|
|
8807
8844
|
subChatEvents,
|
|
8808
|
-
appSettingsMemoryRequests
|
|
8845
|
+
appSettingsMemoryRequests,
|
|
8846
|
+
tokenUsage,
|
|
8847
|
+
promptBudget
|
|
8809
8848
|
};
|
|
8810
8849
|
}
|
|
8811
8850
|
} finally {
|
|
@@ -8822,6 +8861,8 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8822
8861
|
assistant = resp.content;
|
|
8823
8862
|
category = resp.category;
|
|
8824
8863
|
modelName = resp.modelName;
|
|
8864
|
+
tokenUsage = resp.tokenUsage;
|
|
8865
|
+
promptBudget = resp.promptBudget;
|
|
8825
8866
|
followUpSuggestions = resp.followUpSuggestions;
|
|
8826
8867
|
taskProposals = resp.taskProposals;
|
|
8827
8868
|
taskUpdateProposals = resp.taskUpdateProposals;
|
|
@@ -8844,7 +8885,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8844
8885
|
taskEvents,
|
|
8845
8886
|
pendingTaskUpdateJobs,
|
|
8846
8887
|
subChatEvents,
|
|
8847
|
-
appSettingsMemoryRequests
|
|
8888
|
+
appSettingsMemoryRequests,
|
|
8889
|
+
tokenUsage,
|
|
8890
|
+
promptBudget
|
|
8848
8891
|
};
|
|
8849
8892
|
}
|
|
8850
8893
|
if (chatKeyBytes) {
|
|
@@ -8898,7 +8941,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
8898
8941
|
taskEvents,
|
|
8899
8942
|
pendingTaskUpdateJobs,
|
|
8900
8943
|
subChatEvents,
|
|
8901
|
-
appSettingsMemoryRequests
|
|
8944
|
+
appSettingsMemoryRequests,
|
|
8945
|
+
tokenUsage,
|
|
8946
|
+
promptBudget
|
|
8902
8947
|
};
|
|
8903
8948
|
}
|
|
8904
8949
|
if (claim.state !== "LEASED" || !leaseToken || leaseGeneration === null || leaseGeneration < 1 || !assistantId || keyVersion === null || keyVersion !== 1 || claim.chat_id !== chatId || claim.turn_id !== savedTurnId || typeof claim.sealed_payload !== "string") {
|
|
@@ -9072,7 +9117,9 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
9072
9117
|
taskEvents,
|
|
9073
9118
|
pendingTaskUpdateJobs,
|
|
9074
9119
|
subChatEvents,
|
|
9075
|
-
appSettingsMemoryRequests
|
|
9120
|
+
appSettingsMemoryRequests,
|
|
9121
|
+
tokenUsage,
|
|
9122
|
+
promptBudget
|
|
9076
9123
|
};
|
|
9077
9124
|
}
|
|
9078
9125
|
async persistEncryptedSystemMessage(ws, systemMessage, targetChatId) {
|
|
@@ -11752,7 +11799,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
11752
11799
|
const session = this.requireSession();
|
|
11753
11800
|
const masterKey = base64ToBytes(session.masterKeyExportedB64);
|
|
11754
11801
|
const cache = await this.ensureSynced();
|
|
11755
|
-
const { createHash:
|
|
11802
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
11756
11803
|
const embed = cache.embeds.find(
|
|
11757
11804
|
(e) => String(e.embed_id ?? "").startsWith(embedIdOrShort) || String(e.id ?? "").startsWith(embedIdOrShort)
|
|
11758
11805
|
);
|
|
@@ -11760,7 +11807,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
11760
11807
|
throw new Error(`Embed '${embedIdOrShort}' not found in local cache.`);
|
|
11761
11808
|
}
|
|
11762
11809
|
const embedId = String(embed.embed_id ?? embed.id ?? "");
|
|
11763
|
-
const hashedEmbedId =
|
|
11810
|
+
const hashedEmbedId = createHash15("sha256").update(embedId).digest("hex");
|
|
11764
11811
|
const embedKeyBytes = await this.resolveEmbedKey(
|
|
11765
11812
|
cache,
|
|
11766
11813
|
masterKey,
|
|
@@ -11868,8 +11915,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
11868
11915
|
if (!embed) {
|
|
11869
11916
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
11870
11917
|
}
|
|
11871
|
-
const { createHash:
|
|
11872
|
-
const hashedEmbedId =
|
|
11918
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
11919
|
+
const hashedEmbedId = createHash15("sha256").update(embedId).digest("hex");
|
|
11873
11920
|
const embedKey = await this.resolveEmbedKey(
|
|
11874
11921
|
cache,
|
|
11875
11922
|
masterKey,
|
|
@@ -11985,8 +12032,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
11985
12032
|
if (!embed) {
|
|
11986
12033
|
throw new Error(`Embed '${embedId}' not found in local cache. Run 'openmates chats list' to sync first.`);
|
|
11987
12034
|
}
|
|
11988
|
-
const { createHash:
|
|
11989
|
-
const hashedEmbedId =
|
|
12035
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
12036
|
+
const hashedEmbedId = createHash15("sha256").update(embedId).digest("hex");
|
|
11990
12037
|
const embedKey = await this.resolveEmbedKey(
|
|
11991
12038
|
cache,
|
|
11992
12039
|
masterKey,
|
|
@@ -12425,9 +12472,9 @@ Required: ${schema.required.join(", ")}`
|
|
|
12425
12472
|
chats.splice(0, chats.length, ...reconciledChats);
|
|
12426
12473
|
if (totalChatCount === 0) totalChatCount = chats.length;
|
|
12427
12474
|
if (reconciliation.deleted_chat_ids?.length) {
|
|
12428
|
-
const { createHash:
|
|
12475
|
+
const { createHash: createHash15 } = await import("crypto");
|
|
12429
12476
|
const deletedHashes = new Set(
|
|
12430
|
-
reconciliation.deleted_chat_ids.map((id) =>
|
|
12477
|
+
reconciliation.deleted_chat_ids.map((id) => createHash15("sha256").update(id).digest("hex"))
|
|
12431
12478
|
);
|
|
12432
12479
|
const keptEmbeds = embeds.filter((embed) => !deletedHashes.has(String(embed.hashed_chat_id ?? "")));
|
|
12433
12480
|
const keptKeys = embedKeys.filter((key) => !deletedHashes.has(String(key.hashed_chat_id ?? "")));
|
|
@@ -21185,7 +21232,7 @@ import { stdin as stdin3, stdout as stdout2 } from "process";
|
|
|
21185
21232
|
import { readFileSync as readFileSync11, realpathSync as realpathSync2, writeFileSync as writeFileSync7 } from "fs";
|
|
21186
21233
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
21187
21234
|
import { basename as basename3, dirname as dirname5 } from "path";
|
|
21188
|
-
import { createHash as
|
|
21235
|
+
import { createHash as createHash14, randomBytes as randomBytes8, randomUUID as randomUUID10 } from "crypto";
|
|
21189
21236
|
import { arch as arch2, platform as platform3 } from "os";
|
|
21190
21237
|
import WebSocket2 from "ws";
|
|
21191
21238
|
|
|
@@ -45994,6 +46041,9 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
45994
46041
|
},
|
|
45995
46042
|
all_examples_subtitle: {
|
|
45996
46043
|
text: "Pick one to see how the workspace feels in practice."
|
|
46044
|
+
},
|
|
46045
|
+
back_to_recent: {
|
|
46046
|
+
text: "Back to recent"
|
|
45997
46047
|
}
|
|
45998
46048
|
}
|
|
45999
46049
|
},
|
|
@@ -46107,6 +46157,51 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
46107
46157
|
}
|
|
46108
46158
|
},
|
|
46109
46159
|
chats: {
|
|
46160
|
+
chat: {
|
|
46161
|
+
welcome: {
|
|
46162
|
+
back_to_recent: {
|
|
46163
|
+
text: "Back to recent"
|
|
46164
|
+
}
|
|
46165
|
+
},
|
|
46166
|
+
sub_chats: {
|
|
46167
|
+
confirmation_title: {
|
|
46168
|
+
text: "Start {count} background chats?"
|
|
46169
|
+
},
|
|
46170
|
+
confirmation_description: {
|
|
46171
|
+
text: "OpenMates starts up to {auto} automatically. Larger batches need your approval, and each chat can have up to {max} direct sub-chats."
|
|
46172
|
+
},
|
|
46173
|
+
confirmation_more: {
|
|
46174
|
+
text: "+{count} more"
|
|
46175
|
+
},
|
|
46176
|
+
start_all: {
|
|
46177
|
+
text: "Start all {count}"
|
|
46178
|
+
},
|
|
46179
|
+
progress_label: {
|
|
46180
|
+
text: "Sequential sub-chats: {completed} / {total} complete"
|
|
46181
|
+
},
|
|
46182
|
+
progress_stopping: {
|
|
46183
|
+
text: "Stopping sequential sub-chats..."
|
|
46184
|
+
},
|
|
46185
|
+
stop_queue: {
|
|
46186
|
+
text: "Stop queue"
|
|
46187
|
+
},
|
|
46188
|
+
broadcast_to_siblings: {
|
|
46189
|
+
text: "Send to sibling sub-chats"
|
|
46190
|
+
},
|
|
46191
|
+
return_to_parent: {
|
|
46192
|
+
text: "Return to parent chat"
|
|
46193
|
+
},
|
|
46194
|
+
start_first: {
|
|
46195
|
+
text: "Start first {count}"
|
|
46196
|
+
},
|
|
46197
|
+
confirmation_failed: {
|
|
46198
|
+
text: "Failed to update sub-chat approval."
|
|
46199
|
+
},
|
|
46200
|
+
limit_exceeded: {
|
|
46201
|
+
text: "This chat has reached its sub-chat limit."
|
|
46202
|
+
}
|
|
46203
|
+
}
|
|
46204
|
+
},
|
|
46110
46205
|
context_menu: {
|
|
46111
46206
|
download: {
|
|
46112
46207
|
text: "Download"
|
|
@@ -46213,46 +46308,6 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
46213
46308
|
text: "Could not start the explanation chat. Please try again."
|
|
46214
46309
|
}
|
|
46215
46310
|
},
|
|
46216
|
-
chat: {
|
|
46217
|
-
sub_chats: {
|
|
46218
|
-
confirmation_title: {
|
|
46219
|
-
text: "Start {count} background chats?"
|
|
46220
|
-
},
|
|
46221
|
-
confirmation_description: {
|
|
46222
|
-
text: "OpenMates starts up to {auto} automatically. Larger batches need your approval, and each chat can have up to {max} direct sub-chats."
|
|
46223
|
-
},
|
|
46224
|
-
confirmation_more: {
|
|
46225
|
-
text: "+{count} more"
|
|
46226
|
-
},
|
|
46227
|
-
start_all: {
|
|
46228
|
-
text: "Start all {count}"
|
|
46229
|
-
},
|
|
46230
|
-
progress_label: {
|
|
46231
|
-
text: "Sequential sub-chats: {completed} / {total} complete"
|
|
46232
|
-
},
|
|
46233
|
-
progress_stopping: {
|
|
46234
|
-
text: "Stopping sequential sub-chats..."
|
|
46235
|
-
},
|
|
46236
|
-
stop_queue: {
|
|
46237
|
-
text: "Stop queue"
|
|
46238
|
-
},
|
|
46239
|
-
broadcast_to_siblings: {
|
|
46240
|
-
text: "Send to sibling sub-chats"
|
|
46241
|
-
},
|
|
46242
|
-
return_to_parent: {
|
|
46243
|
-
text: "Return to parent chat"
|
|
46244
|
-
},
|
|
46245
|
-
start_first: {
|
|
46246
|
-
text: "Start first {count}"
|
|
46247
|
-
},
|
|
46248
|
-
confirmation_failed: {
|
|
46249
|
-
text: "Failed to update sub-chat approval."
|
|
46250
|
-
},
|
|
46251
|
-
limit_exceeded: {
|
|
46252
|
-
text: "This chat has reached its sub-chat limit."
|
|
46253
|
-
}
|
|
46254
|
-
}
|
|
46255
|
-
},
|
|
46256
46311
|
hidden_chats: {
|
|
46257
46312
|
unlock: {
|
|
46258
46313
|
text: "Unlock hidden chats"
|
|
@@ -62823,14 +62878,126 @@ function buildAssistantFeedbackDecision(rating) {
|
|
|
62823
62878
|
}
|
|
62824
62879
|
|
|
62825
62880
|
// src/benchmark.ts
|
|
62826
|
-
import { randomUUID as randomUUID8 } from "crypto";
|
|
62881
|
+
import { createHash as createHash13, randomUUID as randomUUID8 } from "crypto";
|
|
62827
62882
|
import { existsSync as existsSync8, mkdtempSync as mkdtempSync2, readFileSync as readFileSync7, readdirSync as readdirSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
62828
62883
|
import { tmpdir } from "os";
|
|
62829
62884
|
import { dirname as dirname4, join as join5, resolve as resolve5 } from "path";
|
|
62830
62885
|
import { fileURLToPath } from "url";
|
|
62831
62886
|
var DEFAULT_JUDGE_MODEL = "google/gemini-3-flash-preview";
|
|
62887
|
+
var DEFAULT_PROMPT_BUDGET_MODEL = "google/gemini-3.5-flash";
|
|
62832
62888
|
var DEFAULT_EXTENSIVE_SIZE = 10;
|
|
62833
62889
|
var DEFAULT_PARALLEL = 4;
|
|
62890
|
+
var PROMPT_BUDGET_TARGET_INPUT_TOKENS_PER_TURN = 16e3;
|
|
62891
|
+
var PROMPT_BUDGET_TARGET_OUTPUT_TOKENS_PER_TURN = 800;
|
|
62892
|
+
var PROMPT_BUDGET_JUDGE_INPUT_TOKENS_PER_RESPONSE = 3e3;
|
|
62893
|
+
var PROMPT_BUDGET_JUDGE_OUTPUT_TOKENS_PER_RESPONSE = 300;
|
|
62894
|
+
var PROMPT_BUDGET_CASES = [
|
|
62895
|
+
{
|
|
62896
|
+
id: "single-writing-concise-email",
|
|
62897
|
+
title: "Concise email rewrite",
|
|
62898
|
+
category: "writing",
|
|
62899
|
+
qualityRubric: "Warm, concise, under 120 words, directly asks to move the Tuesday sync to Thursday afternoon.",
|
|
62900
|
+
turns: [
|
|
62901
|
+
"Write a concise but warm email to a colleague asking to move our Tuesday 10:00 project sync to Thursday afternoon. Keep it under 120 words."
|
|
62902
|
+
]
|
|
62903
|
+
},
|
|
62904
|
+
{
|
|
62905
|
+
id: "single-code-palindrome",
|
|
62906
|
+
title: "TypeScript palindrome helper",
|
|
62907
|
+
category: "coding",
|
|
62908
|
+
qualityRubric: "Produces valid TypeScript, ignores spaces/punctuation/case, includes one short usage example, and avoids unrelated prose.",
|
|
62909
|
+
turns: [
|
|
62910
|
+
"Write a TypeScript function isPalindrome(input: string): boolean that ignores spaces, punctuation, and case. Include only the function and one short usage example."
|
|
62911
|
+
]
|
|
62912
|
+
},
|
|
62913
|
+
{
|
|
62914
|
+
id: "single-current-web-ai-news",
|
|
62915
|
+
title: "Current AI release search",
|
|
62916
|
+
category: "web",
|
|
62917
|
+
qualityRubric: "Uses current web/search behavior, cites sources, and summarizes a major AI model release from the last 30 days without fabricating certainty.",
|
|
62918
|
+
turns: [
|
|
62919
|
+
"Search the web for the latest major AI model release from the last 30 days and summarize what changed, with sources."
|
|
62920
|
+
]
|
|
62921
|
+
},
|
|
62922
|
+
{
|
|
62923
|
+
id: "single-travel-filtered-flight",
|
|
62924
|
+
title: "Filtered flight search",
|
|
62925
|
+
category: "travel",
|
|
62926
|
+
qualityRubric: "Attempts appropriate travel/tool routing, honors direct flight/date/budget constraints where possible, and explains tradeoffs or uncertainty.",
|
|
62927
|
+
turns: [
|
|
62928
|
+
"Find direct flights from Berlin to Barcelona on 2026-09-10 under 220 EUR if possible, and explain the best tradeoff."
|
|
62929
|
+
]
|
|
62930
|
+
},
|
|
62931
|
+
{
|
|
62932
|
+
id: "single-events-accessibility",
|
|
62933
|
+
title: "Accessible family events",
|
|
62934
|
+
category: "events",
|
|
62935
|
+
qualityRubric: "Attempts event/tool routing, separates confirmed from unknown accessibility, and avoids claiming accessibility without evidence.",
|
|
62936
|
+
turns: [
|
|
62937
|
+
"Find wheelchair-accessible family events in Berlin this weekend. Separate confirmed accessibility from unknown accessibility."
|
|
62938
|
+
]
|
|
62939
|
+
},
|
|
62940
|
+
{
|
|
62941
|
+
id: "single-openmates-help",
|
|
62942
|
+
title: "OpenMates skill selection",
|
|
62943
|
+
category: "openmates",
|
|
62944
|
+
qualityRubric: "Correctly explains a suitable OpenMates app/skill path for product comparison and suggests a useful next step.",
|
|
62945
|
+
turns: [
|
|
62946
|
+
"Explain which OpenMates app or skill I should use to compare products before buying a new laptop, and what you would do next."
|
|
62947
|
+
]
|
|
62948
|
+
},
|
|
62949
|
+
{
|
|
62950
|
+
id: "single-safety-privacy-review",
|
|
62951
|
+
title: "Privacy review",
|
|
62952
|
+
category: "safety",
|
|
62953
|
+
qualityRubric: "Identifies privacy risks in benchmark logging and gives practical safer defaults without overclaiming compliance.",
|
|
62954
|
+
turns: [
|
|
62955
|
+
"Review this benchmark design for privacy risks: it logs prompts, outputs, model ids, usage costs, and debug metadata to a shared file. List risks and safer defaults."
|
|
62956
|
+
]
|
|
62957
|
+
},
|
|
62958
|
+
{
|
|
62959
|
+
id: "multiturn-model-evaluation-plan",
|
|
62960
|
+
title: "Model evaluation follow-up plan",
|
|
62961
|
+
category: "planning",
|
|
62962
|
+
qualityRubric: "Maintains continuity across all six turns, refines prior outputs instead of restarting, and produces concrete evaluation criteria.",
|
|
62963
|
+
turns: [
|
|
62964
|
+
"Create a three-step plan for evaluating whether a new AI model is ready for production use.",
|
|
62965
|
+
"Make step 2 more concrete with two measurable checks.",
|
|
62966
|
+
"Add a risk register with severity and mitigation for the top three risks.",
|
|
62967
|
+
"Turn the plan into a one-week execution schedule.",
|
|
62968
|
+
"Identify which tasks could be automated with OpenMates skills.",
|
|
62969
|
+
"Summarize the final plan in one sentence."
|
|
62970
|
+
]
|
|
62971
|
+
},
|
|
62972
|
+
{
|
|
62973
|
+
id: "multiturn-startup-launch",
|
|
62974
|
+
title: "Freelancer AI assistant launch",
|
|
62975
|
+
category: "business",
|
|
62976
|
+
qualityRubric: "Builds on previous launch context, keeps the privacy-first freelancer positioning, and produces specific GTM artifacts.",
|
|
62977
|
+
turns: [
|
|
62978
|
+
"Help me plan the launch of a privacy-first personal AI assistant for freelancers.",
|
|
62979
|
+
"Define the ideal first customer profile and their top pains.",
|
|
62980
|
+
"Draft a landing page hero section for that customer.",
|
|
62981
|
+
"Create a simple pricing experiment with three tiers.",
|
|
62982
|
+
"Write five outbound messages for early customer interviews.",
|
|
62983
|
+
"Summarize the riskiest assumption and how to test it this week."
|
|
62984
|
+
]
|
|
62985
|
+
},
|
|
62986
|
+
{
|
|
62987
|
+
id: "multiturn-debugging-workflow",
|
|
62988
|
+
title: "SvelteKit stale chat debugging",
|
|
62989
|
+
category: "debugging",
|
|
62990
|
+
qualityRubric: "Maintains the stale-chat bug context, distinguishes frontend cache from backend sync, and uses data-testid selector guidance.",
|
|
62991
|
+
turns: [
|
|
62992
|
+
"A SvelteKit page sometimes shows stale chat messages after refresh. Give me a debugging plan.",
|
|
62993
|
+
"Add likely root causes involving IndexedDB and WebSocket sync.",
|
|
62994
|
+
"Suggest instrumentation I should add before fixing code.",
|
|
62995
|
+
"Write a minimal regression test outline using data-testid selectors.",
|
|
62996
|
+
"Explain how to tell if the bug is backend sync or frontend cache.",
|
|
62997
|
+
"Condense this into a checklist I can hand to another engineer."
|
|
62998
|
+
]
|
|
62999
|
+
}
|
|
63000
|
+
];
|
|
62834
63001
|
var FIXTURE_IMAGE_SVG = `<?xml version="1.0" encoding="UTF-8"?>
|
|
62835
63002
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="800" viewBox="0 0 1200 800">
|
|
62836
63003
|
<rect width="1200" height="800" fill="#d8ecff"/>
|
|
@@ -63101,6 +63268,10 @@ async function handleBenchmark(client, subcommand, rest, flags) {
|
|
|
63101
63268
|
printBenchmarkHelp();
|
|
63102
63269
|
return;
|
|
63103
63270
|
}
|
|
63271
|
+
if (subcommand === "prompt-budget") {
|
|
63272
|
+
await handlePromptBudgetBenchmark(client, rest, flags);
|
|
63273
|
+
return;
|
|
63274
|
+
}
|
|
63104
63275
|
if (subcommand !== "model") {
|
|
63105
63276
|
throw new Error(`Unknown benchmark command '${subcommand}'. Run 'openmates benchmark --help'.`);
|
|
63106
63277
|
}
|
|
@@ -63175,6 +63346,8 @@ async function handleBenchmark(client, subcommand, rest, flags) {
|
|
|
63175
63346
|
function printBenchmarkHelp() {
|
|
63176
63347
|
console.log(`Benchmark commands:
|
|
63177
63348
|
openmates benchmark model <provider/model> [provider/model...] --confirm-spend-credits [--compare] [--suite quick|extensive|all] [--json]
|
|
63349
|
+
openmates benchmark prompt-budget --phase baseline|postchange --confirm-spend-credits [--model provider/model] [--resume-from <path>] [--json]
|
|
63350
|
+
openmates benchmark prompt-budget compare --baseline <path> --postchange <path> [--json]
|
|
63178
63351
|
|
|
63179
63352
|
Runs real incognito chat requests through the OpenMates product path. Live runs
|
|
63180
63353
|
spend the logged-in user's credits and usage entries are grouped as benchmark spend.
|
|
@@ -63194,6 +63367,74 @@ Options:
|
|
|
63194
63367
|
--output <path> Save JSON result to a file
|
|
63195
63368
|
--json Print JSON result`);
|
|
63196
63369
|
}
|
|
63370
|
+
async function handlePromptBudgetBenchmark(client, rest, flags) {
|
|
63371
|
+
if (rest[0] === "compare") {
|
|
63372
|
+
writePromptBudgetComparison(comparePromptBudgetArtifacts(flags), flags, stringFlag(flags.output));
|
|
63373
|
+
return;
|
|
63374
|
+
}
|
|
63375
|
+
if (rest.length > 0) {
|
|
63376
|
+
throw new Error(`Unknown prompt-budget benchmark argument '${rest[0]}'. Run 'openmates benchmark --help'.`);
|
|
63377
|
+
}
|
|
63378
|
+
const phase = parsePromptBudgetPhase(flags.phase);
|
|
63379
|
+
const targetModel = stringFlag(flags.model) ?? DEFAULT_PROMPT_BUDGET_MODEL;
|
|
63380
|
+
const judgeModel = stringFlag(flags["judge-model"]) ?? DEFAULT_JUDGE_MODEL;
|
|
63381
|
+
const output = stringFlag(flags.output);
|
|
63382
|
+
const resumeFrom = stringFlag(flags["resume-from"]);
|
|
63383
|
+
const dryRun = flags["dry-run"] === true;
|
|
63384
|
+
const responseTimeoutMs = parseResponseTimeoutMs(flags["response-timeout-seconds"]);
|
|
63385
|
+
if (!dryRun && flags["confirm-spend-credits"] !== true) {
|
|
63386
|
+
throw new Error(
|
|
63387
|
+
"Prompt-budget benchmark runs spend real credits from the logged-in account. Rerun with --confirm-spend-credits, or use --dry-run to preview the plan."
|
|
63388
|
+
);
|
|
63389
|
+
}
|
|
63390
|
+
if (dryRun && resumeFrom) {
|
|
63391
|
+
throw new Error("--resume-from is only supported for live prompt-budget runs");
|
|
63392
|
+
}
|
|
63393
|
+
const pricing = loadPricingForModels([targetModel, judgeModel]);
|
|
63394
|
+
const estimate = estimatePromptBudgetCredits(PROMPT_BUDGET_CASES, targetModel, judgeModel, pricing);
|
|
63395
|
+
const resumeArtifact = resumeFrom ? readPromptBudgetResult(resumeFrom) : null;
|
|
63396
|
+
if (resumeArtifact) {
|
|
63397
|
+
validatePromptBudgetResumeArtifact(resumeArtifact, { phase, targetModel, judgeModel });
|
|
63398
|
+
}
|
|
63399
|
+
const runId = stringFlag(flags["run-id"]) ?? resumeArtifact?.runId ?? randomUUID8();
|
|
63400
|
+
const result = makePromptBudgetBaseResult({
|
|
63401
|
+
phase,
|
|
63402
|
+
runId,
|
|
63403
|
+
targetModel,
|
|
63404
|
+
judgeModel,
|
|
63405
|
+
dryRun,
|
|
63406
|
+
estimate
|
|
63407
|
+
});
|
|
63408
|
+
if (resumeArtifact) {
|
|
63409
|
+
result.cases = resumeArtifact.cases.filter(isCompletePromptBudgetCase);
|
|
63410
|
+
recomputePromptBudgetResult(result);
|
|
63411
|
+
}
|
|
63412
|
+
if (dryRun) {
|
|
63413
|
+
writePromptBudgetResult(result, flags, output);
|
|
63414
|
+
return;
|
|
63415
|
+
}
|
|
63416
|
+
if (!client.hasSession()) {
|
|
63417
|
+
throw new Error("Prompt-budget benchmark runs require login. Run 'openmates login' first.");
|
|
63418
|
+
}
|
|
63419
|
+
const completedCaseIds = new Set(result.cases.map((benchmarkCase) => benchmarkCase.id));
|
|
63420
|
+
for (const benchmarkCase of PROMPT_BUDGET_CASES) {
|
|
63421
|
+
if (completedCaseIds.has(benchmarkCase.id)) continue;
|
|
63422
|
+
const caseResult = await runPromptBudgetCase({
|
|
63423
|
+
client,
|
|
63424
|
+
benchmarkCase,
|
|
63425
|
+
phase,
|
|
63426
|
+
runId,
|
|
63427
|
+
targetModel,
|
|
63428
|
+
judgeModel,
|
|
63429
|
+
responseTimeoutMs
|
|
63430
|
+
});
|
|
63431
|
+
result.cases.push(caseResult);
|
|
63432
|
+
recomputePromptBudgetResult(result);
|
|
63433
|
+
if (caseResult.error) break;
|
|
63434
|
+
}
|
|
63435
|
+
recomputePromptBudgetResult(result);
|
|
63436
|
+
writePromptBudgetResult(result, flags, output);
|
|
63437
|
+
}
|
|
63197
63438
|
function parseSuites(value) {
|
|
63198
63439
|
if (value === void 0 || value === false) return ["quick"];
|
|
63199
63440
|
if (value === true) throw new Error("--suite requires a value");
|
|
@@ -63240,6 +63481,24 @@ function parseCaseIds(value) {
|
|
|
63240
63481
|
if (caseIds.length === 0) throw new Error("--case requires at least one case id");
|
|
63241
63482
|
return [...new Set(caseIds)];
|
|
63242
63483
|
}
|
|
63484
|
+
function parsePromptBudgetPhase(value) {
|
|
63485
|
+
if (value === void 0 || value === false) return "baseline";
|
|
63486
|
+
if (value === true) throw new Error("--phase requires baseline or postchange");
|
|
63487
|
+
if (value === "baseline" || value === "postchange") return value;
|
|
63488
|
+
throw new Error("--phase must be baseline or postchange");
|
|
63489
|
+
}
|
|
63490
|
+
function parseResponseTimeoutMs(value) {
|
|
63491
|
+
if (value === void 0 || value === false) return void 0;
|
|
63492
|
+
if (value === true) throw new Error("--response-timeout-seconds requires a value");
|
|
63493
|
+
const parsed = Number.parseInt(value, 10);
|
|
63494
|
+
if (!Number.isInteger(parsed) || parsed < 30 || parsed > 1200) {
|
|
63495
|
+
throw new Error("--response-timeout-seconds must be an integer from 30 to 1200");
|
|
63496
|
+
}
|
|
63497
|
+
return parsed * 1e3;
|
|
63498
|
+
}
|
|
63499
|
+
function stringFlag(value) {
|
|
63500
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
63501
|
+
}
|
|
63243
63502
|
function filterCases(cases, caseIds) {
|
|
63244
63503
|
if (caseIds.length === 0) return cases;
|
|
63245
63504
|
const availableIds = new Set(cases.map((benchmarkCase) => benchmarkCase.id));
|
|
@@ -63296,6 +63555,95 @@ function dedupeCases(cases) {
|
|
|
63296
63555
|
}
|
|
63297
63556
|
return result;
|
|
63298
63557
|
}
|
|
63558
|
+
async function runPromptBudgetCase(params) {
|
|
63559
|
+
const { client, benchmarkCase, phase, runId, targetModel, judgeModel, responseTimeoutMs } = params;
|
|
63560
|
+
const startedAt = Date.now();
|
|
63561
|
+
const history = [];
|
|
63562
|
+
const turns = [];
|
|
63563
|
+
let chatId;
|
|
63564
|
+
try {
|
|
63565
|
+
for (const [index, prompt] of benchmarkCase.turns.entries()) {
|
|
63566
|
+
const turnNumber = index + 1;
|
|
63567
|
+
const rawPrompt = `${modelMention(targetModel)} ${prompt}`;
|
|
63568
|
+
const turnStartedAt = Date.now();
|
|
63569
|
+
const response = await client.sendMessage({
|
|
63570
|
+
message: rawPrompt,
|
|
63571
|
+
chatId,
|
|
63572
|
+
incognito: true,
|
|
63573
|
+
autoApproveSubChats: true,
|
|
63574
|
+
benchmarkMetadata: benchmarkMetadata({
|
|
63575
|
+
runId,
|
|
63576
|
+
suite: `prompt_budget_${phase}`,
|
|
63577
|
+
caseId: `${benchmarkCase.id}:turn-${turnNumber}`,
|
|
63578
|
+
targetModel,
|
|
63579
|
+
judgeModel
|
|
63580
|
+
}),
|
|
63581
|
+
messageHistory: history,
|
|
63582
|
+
precollectResponse: true,
|
|
63583
|
+
responseTimeoutMs
|
|
63584
|
+
});
|
|
63585
|
+
chatId = response.chatId;
|
|
63586
|
+
const turnResult = {
|
|
63587
|
+
turn: turnNumber,
|
|
63588
|
+
prompt,
|
|
63589
|
+
promptHash: stableHash(prompt),
|
|
63590
|
+
assistant: response.assistant,
|
|
63591
|
+
durationMs: Date.now() - turnStartedAt,
|
|
63592
|
+
chatId: response.chatId,
|
|
63593
|
+
messageId: response.messageId,
|
|
63594
|
+
modelName: response.modelName,
|
|
63595
|
+
mateName: response.mateName,
|
|
63596
|
+
category: response.category,
|
|
63597
|
+
tokenUsage: response.tokenUsage,
|
|
63598
|
+
promptBudget: response.promptBudget
|
|
63599
|
+
};
|
|
63600
|
+
turnResult.judge = await judgePromptBudgetTurn({
|
|
63601
|
+
client,
|
|
63602
|
+
benchmarkCase,
|
|
63603
|
+
turn: turnResult,
|
|
63604
|
+
turnNumber,
|
|
63605
|
+
targetModel,
|
|
63606
|
+
judgeModel,
|
|
63607
|
+
runId,
|
|
63608
|
+
phase
|
|
63609
|
+
});
|
|
63610
|
+
turns.push(turnResult);
|
|
63611
|
+
appendHistory(history, "user", rawPrompt);
|
|
63612
|
+
appendHistory(history, "assistant", response.assistant);
|
|
63613
|
+
}
|
|
63614
|
+
const continuityJudge = benchmarkCase.turns.length > 1 ? await judgePromptBudgetContinuity({
|
|
63615
|
+
client,
|
|
63616
|
+
benchmarkCase,
|
|
63617
|
+
turns,
|
|
63618
|
+
targetModel,
|
|
63619
|
+
judgeModel,
|
|
63620
|
+
runId,
|
|
63621
|
+
phase
|
|
63622
|
+
}) : void 0;
|
|
63623
|
+
const passed = turns.every((turn) => (turn.judge?.score ?? 0) >= 4) && (!continuityJudge || (continuityJudge.score ?? 0) >= 4);
|
|
63624
|
+
return {
|
|
63625
|
+
id: benchmarkCase.id,
|
|
63626
|
+
title: benchmarkCase.title,
|
|
63627
|
+
category: benchmarkCase.category,
|
|
63628
|
+
promptHashes: benchmarkCase.turns.map(stableHash),
|
|
63629
|
+
turns,
|
|
63630
|
+
continuityJudge,
|
|
63631
|
+
passed,
|
|
63632
|
+
durationMs: Date.now() - startedAt
|
|
63633
|
+
};
|
|
63634
|
+
} catch (error) {
|
|
63635
|
+
return {
|
|
63636
|
+
id: benchmarkCase.id,
|
|
63637
|
+
title: benchmarkCase.title,
|
|
63638
|
+
category: benchmarkCase.category,
|
|
63639
|
+
promptHashes: benchmarkCase.turns.map(stableHash),
|
|
63640
|
+
turns,
|
|
63641
|
+
passed: false,
|
|
63642
|
+
durationMs: Date.now() - startedAt,
|
|
63643
|
+
error: error instanceof Error ? error.message : String(error)
|
|
63644
|
+
};
|
|
63645
|
+
}
|
|
63646
|
+
}
|
|
63299
63647
|
async function runCaseJob(params) {
|
|
63300
63648
|
const { client, job, judgeModel, runId, imagePath } = params;
|
|
63301
63649
|
const { model, benchmarkCase } = job;
|
|
@@ -63488,6 +63836,82 @@ async function judgeCase(params) {
|
|
|
63488
63836
|
durationMs: Date.now() - startedAt
|
|
63489
63837
|
};
|
|
63490
63838
|
}
|
|
63839
|
+
async function judgePromptBudgetTurn(params) {
|
|
63840
|
+
const startedAt = Date.now();
|
|
63841
|
+
const judgeResponse = await params.client.sendMessage({
|
|
63842
|
+
message: `${modelMention(params.judgeModel)} ${promptBudgetTurnJudgePrompt(params)}`,
|
|
63843
|
+
incognito: true,
|
|
63844
|
+
autoApproveSubChats: true,
|
|
63845
|
+
benchmarkMetadata: benchmarkMetadata({
|
|
63846
|
+
runId: params.runId,
|
|
63847
|
+
suite: `prompt_budget_${params.phase}`,
|
|
63848
|
+
caseId: `${params.benchmarkCase.id}:turn-${params.turnNumber}:judge`,
|
|
63849
|
+
targetModel: params.targetModel,
|
|
63850
|
+
judgeModel: params.judgeModel
|
|
63851
|
+
}),
|
|
63852
|
+
precollectResponse: true
|
|
63853
|
+
});
|
|
63854
|
+
const judgment = parseJudgment(judgeResponse.assistant);
|
|
63855
|
+
return {
|
|
63856
|
+
model: params.judgeModel,
|
|
63857
|
+
score: judgment.score,
|
|
63858
|
+
reason: judgment.reason,
|
|
63859
|
+
raw: judgeResponse.assistant,
|
|
63860
|
+
durationMs: Date.now() - startedAt
|
|
63861
|
+
};
|
|
63862
|
+
}
|
|
63863
|
+
async function judgePromptBudgetContinuity(params) {
|
|
63864
|
+
const startedAt = Date.now();
|
|
63865
|
+
const judgeResponse = await params.client.sendMessage({
|
|
63866
|
+
message: `${modelMention(params.judgeModel)} ${promptBudgetContinuityJudgePrompt(params)}`,
|
|
63867
|
+
incognito: true,
|
|
63868
|
+
autoApproveSubChats: true,
|
|
63869
|
+
benchmarkMetadata: benchmarkMetadata({
|
|
63870
|
+
runId: params.runId,
|
|
63871
|
+
suite: `prompt_budget_${params.phase}`,
|
|
63872
|
+
caseId: `${params.benchmarkCase.id}:continuity-judge`,
|
|
63873
|
+
targetModel: params.targetModel,
|
|
63874
|
+
judgeModel: params.judgeModel
|
|
63875
|
+
}),
|
|
63876
|
+
precollectResponse: true
|
|
63877
|
+
});
|
|
63878
|
+
const judgment = parseJudgment(judgeResponse.assistant);
|
|
63879
|
+
return {
|
|
63880
|
+
model: params.judgeModel,
|
|
63881
|
+
score: judgment.score,
|
|
63882
|
+
reason: judgment.reason,
|
|
63883
|
+
raw: judgeResponse.assistant,
|
|
63884
|
+
durationMs: Date.now() - startedAt
|
|
63885
|
+
};
|
|
63886
|
+
}
|
|
63887
|
+
function promptBudgetTurnJudgePrompt(params) {
|
|
63888
|
+
return [
|
|
63889
|
+
"You are judging a real OpenMates prompt-budget benchmark response.",
|
|
63890
|
+
"Return exactly two plain-text lines, with no markdown, no code block, and no tool use.",
|
|
63891
|
+
"Line 1 format: BENCHMARK_SCORE=<integer from 1 to 5>",
|
|
63892
|
+
"Line 2 format: BENCHMARK_REASON=<one short sentence>",
|
|
63893
|
+
"Score for helpfulness, instruction following, factuality, safety, routing/tool appropriateness, concision, and whether the answer preserves needed context.",
|
|
63894
|
+
`Target model: ${params.targetModel}`,
|
|
63895
|
+
`Case: ${params.benchmarkCase.id}`,
|
|
63896
|
+
`Turn: ${params.turnNumber} of ${params.benchmarkCase.turns.length}`,
|
|
63897
|
+
`Rubric: ${params.benchmarkCase.qualityRubric}`,
|
|
63898
|
+
`User prompt: ${JSON.stringify(params.turn.prompt)}`,
|
|
63899
|
+
`Assistant response: ${JSON.stringify(params.turn.assistant)}`
|
|
63900
|
+
].join("\n");
|
|
63901
|
+
}
|
|
63902
|
+
function promptBudgetContinuityJudgePrompt(params) {
|
|
63903
|
+
return [
|
|
63904
|
+
"You are judging multi-turn continuity for a real OpenMates prompt-budget benchmark conversation.",
|
|
63905
|
+
"Return exactly two plain-text lines, with no markdown, no code block, and no tool use.",
|
|
63906
|
+
"Line 1 format: BENCHMARK_SCORE=<integer from 1 to 5>",
|
|
63907
|
+
"Line 2 format: BENCHMARK_REASON=<one short sentence>",
|
|
63908
|
+
"Score whether the assistant consistently builds on earlier turns, avoids restarting, and satisfies the final requested artifact.",
|
|
63909
|
+
`Target model: ${params.targetModel}`,
|
|
63910
|
+
`Case: ${params.benchmarkCase.id}`,
|
|
63911
|
+
`Rubric: ${params.benchmarkCase.qualityRubric}`,
|
|
63912
|
+
`Turns: ${JSON.stringify(params.turns.map((turn) => ({ prompt: turn.prompt, assistant: turn.assistant })))}`
|
|
63913
|
+
].join("\n");
|
|
63914
|
+
}
|
|
63491
63915
|
async function runPool(items, parallel, worker) {
|
|
63492
63916
|
let index = 0;
|
|
63493
63917
|
const workers = Array.from({ length: Math.min(parallel, items.length) }, async () => {
|
|
@@ -63725,6 +64149,27 @@ function estimateCredits(cases, targetModels, judgeModel, pricing) {
|
|
|
63725
64149
|
assumptions: { targetInputTokens, targetOutputTokens, judgeInputTokens, judgeOutputTokens }
|
|
63726
64150
|
};
|
|
63727
64151
|
}
|
|
64152
|
+
function estimatePromptBudgetCredits(cases, targetModel, judgeModel, pricing) {
|
|
64153
|
+
const targetPricing = pricing.get(targetModel);
|
|
64154
|
+
const judgePricing = pricing.get(judgeModel);
|
|
64155
|
+
if (!targetPricing || !judgePricing) {
|
|
64156
|
+
throw new Error("Prompt-budget benchmark pricing preflight failed after model pricing lookup.");
|
|
64157
|
+
}
|
|
64158
|
+
const targetTurnCount = cases.reduce((sum, benchmarkCase) => sum + benchmarkCase.turns.length, 0);
|
|
64159
|
+
const judgeTurnCount = targetTurnCount + cases.filter((benchmarkCase) => benchmarkCase.turns.length > 1).length;
|
|
64160
|
+
const targetInputTokens = targetTurnCount * PROMPT_BUDGET_TARGET_INPUT_TOKENS_PER_TURN;
|
|
64161
|
+
const targetOutputTokens = targetTurnCount * PROMPT_BUDGET_TARGET_OUTPUT_TOKENS_PER_TURN;
|
|
64162
|
+
const judgeInputTokens = judgeTurnCount * PROMPT_BUDGET_JUDGE_INPUT_TOKENS_PER_RESPONSE;
|
|
64163
|
+
const judgeOutputTokens = judgeTurnCount * PROMPT_BUDGET_JUDGE_OUTPUT_TOKENS_PER_RESPONSE;
|
|
64164
|
+
const targetCredits = creditsFor(targetPricing, targetInputTokens, targetOutputTokens);
|
|
64165
|
+
const judgeCredits = creditsFor(judgePricing, judgeInputTokens, judgeOutputTokens);
|
|
64166
|
+
return {
|
|
64167
|
+
targetCredits,
|
|
64168
|
+
judgeCredits,
|
|
64169
|
+
totalCredits: targetCredits + judgeCredits,
|
|
64170
|
+
assumptions: { targetInputTokens, targetOutputTokens, judgeInputTokens, judgeOutputTokens }
|
|
64171
|
+
};
|
|
64172
|
+
}
|
|
63728
64173
|
function creditsFor(pricing, inputTokens, outputTokens) {
|
|
63729
64174
|
return Math.ceil(inputTokens / pricing.inputTokensPerCredit) + Math.ceil(outputTokens / pricing.outputTokensPerCredit);
|
|
63730
64175
|
}
|
|
@@ -63762,6 +64207,21 @@ function makeBaseResult(params) {
|
|
|
63762
64207
|
}
|
|
63763
64208
|
};
|
|
63764
64209
|
}
|
|
64210
|
+
function makePromptBudgetBaseResult(params) {
|
|
64211
|
+
return {
|
|
64212
|
+
command: "benchmark prompt-budget",
|
|
64213
|
+
status: params.dryRun ? "planned" : "completed",
|
|
64214
|
+
phase: params.phase,
|
|
64215
|
+
runId: params.runId,
|
|
64216
|
+
corpusHash: hashPromptBudgetCorpus(PROMPT_BUDGET_CASES),
|
|
64217
|
+
targetModel: params.targetModel,
|
|
64218
|
+
judgeModel: params.judgeModel,
|
|
64219
|
+
spendsCredits: !params.dryRun,
|
|
64220
|
+
estimatedCredits: params.estimate,
|
|
64221
|
+
cases: [],
|
|
64222
|
+
summary: promptBudgetSummary([])
|
|
64223
|
+
};
|
|
64224
|
+
}
|
|
63765
64225
|
function recomputeResult(result, totalJobs, interrupted) {
|
|
63766
64226
|
const completed = result.cases.length;
|
|
63767
64227
|
const passed = result.cases.filter((caseResult) => caseResult.passed).length;
|
|
@@ -63804,6 +64264,56 @@ function buildComparison(summaries) {
|
|
|
63804
64264
|
function round2(value) {
|
|
63805
64265
|
return Math.round(value * 100) / 100;
|
|
63806
64266
|
}
|
|
64267
|
+
function recomputePromptBudgetResult(result) {
|
|
64268
|
+
result.summary = promptBudgetSummary(result.cases);
|
|
64269
|
+
result.status = result.summary.completedTurns < result.summary.totalTurns || result.cases.some((benchmarkCase) => Boolean(benchmarkCase.error)) ? "partial" : "completed";
|
|
64270
|
+
}
|
|
64271
|
+
function promptBudgetSummary(cases) {
|
|
64272
|
+
const completedTurns = cases.reduce((sum, benchmarkCase) => sum + benchmarkCase.turns.length, 0);
|
|
64273
|
+
const durations = cases.flatMap((benchmarkCase) => benchmarkCase.turns.map((turn) => turn.durationMs)).filter((value) => value > 0);
|
|
64274
|
+
const judgeScores = cases.flatMap((benchmarkCase) => benchmarkCase.turns.map((turn) => turn.judge?.score)).filter((score) => typeof score === "number" && Number.isFinite(score));
|
|
64275
|
+
const continuityScores = cases.map((benchmarkCase) => benchmarkCase.continuityJudge?.score).filter((score) => typeof score === "number" && Number.isFinite(score));
|
|
64276
|
+
return {
|
|
64277
|
+
totalCases: PROMPT_BUDGET_CASES.length,
|
|
64278
|
+
completedCases: cases.length,
|
|
64279
|
+
totalTurns: PROMPT_BUDGET_CASES.reduce((sum, benchmarkCase) => sum + benchmarkCase.turns.length, 0),
|
|
64280
|
+
completedTurns,
|
|
64281
|
+
passedCases: cases.filter((benchmarkCase) => benchmarkCase.passed).length,
|
|
64282
|
+
failedCases: cases.filter((benchmarkCase) => !benchmarkCase.passed).length,
|
|
64283
|
+
averageTurnDurationMs: durations.length > 0 ? Math.round(durations.reduce((sum, value) => sum + value, 0) / durations.length) : null,
|
|
64284
|
+
averageJudgeScore: judgeScores.length > 0 ? round2(judgeScores.reduce((sum, score) => sum + score, 0) / judgeScores.length) : null,
|
|
64285
|
+
averageContinuityScore: continuityScores.length > 0 ? round2(continuityScores.reduce((sum, score) => sum + score, 0) / continuityScores.length) : null
|
|
64286
|
+
};
|
|
64287
|
+
}
|
|
64288
|
+
function validatePromptBudgetResumeArtifact(artifact, expected) {
|
|
64289
|
+
if (artifact.command !== "benchmark prompt-budget") {
|
|
64290
|
+
throw new Error("--resume-from must point to a prompt-budget benchmark artifact");
|
|
64291
|
+
}
|
|
64292
|
+
if (artifact.corpusHash !== hashPromptBudgetCorpus(PROMPT_BUDGET_CASES)) {
|
|
64293
|
+
throw new Error("--resume-from artifact uses a different prompt-budget corpus");
|
|
64294
|
+
}
|
|
64295
|
+
if (artifact.phase !== expected.phase) {
|
|
64296
|
+
throw new Error(`--resume-from artifact phase is ${artifact.phase}, expected ${expected.phase}`);
|
|
64297
|
+
}
|
|
64298
|
+
if (artifact.targetModel !== expected.targetModel || artifact.judgeModel !== expected.judgeModel) {
|
|
64299
|
+
throw new Error("--resume-from artifact target or judge model does not match this run");
|
|
64300
|
+
}
|
|
64301
|
+
}
|
|
64302
|
+
function isCompletePromptBudgetCase(caseResult) {
|
|
64303
|
+
if (caseResult.error) return false;
|
|
64304
|
+
const definition = PROMPT_BUDGET_CASES.find((benchmarkCase) => benchmarkCase.id === caseResult.id);
|
|
64305
|
+
if (!definition) return false;
|
|
64306
|
+
if (caseResult.promptHashes.join("|") !== definition.turns.map(stableHash).join("|")) return false;
|
|
64307
|
+
if (caseResult.turns.length !== definition.turns.length) return false;
|
|
64308
|
+
if (caseResult.turns.some((turn) => !turn.judge)) return false;
|
|
64309
|
+
return definition.turns.length === 1 || Boolean(caseResult.continuityJudge);
|
|
64310
|
+
}
|
|
64311
|
+
function hashPromptBudgetCorpus(cases) {
|
|
64312
|
+
return stableHash(cases.map((benchmarkCase) => ({ id: benchmarkCase.id, turns: benchmarkCase.turns })));
|
|
64313
|
+
}
|
|
64314
|
+
function stableHash(value) {
|
|
64315
|
+
return createHash13("sha256").update(JSON.stringify(value)).digest("hex");
|
|
64316
|
+
}
|
|
63807
64317
|
function defaultImageFixturePath() {
|
|
63808
64318
|
const fixtureDir = join5(dirname4(fileURLToPath(import.meta.url)), "..", "fixtures");
|
|
63809
64319
|
const fixturePath = join5(fixtureDir, "brandenburger-tor.png");
|
|
@@ -63837,6 +64347,143 @@ function writeBenchmarkResult(result, flags, output) {
|
|
|
63837
64347
|
}
|
|
63838
64348
|
}
|
|
63839
64349
|
}
|
|
64350
|
+
function writePromptBudgetResult(result, flags, output) {
|
|
64351
|
+
const json = `${JSON.stringify(result, null, 2)}
|
|
64352
|
+
`;
|
|
64353
|
+
if (output) writeFileSync5(output, json, "utf-8");
|
|
64354
|
+
if (flags.json === true || output) {
|
|
64355
|
+
process.stdout.write(json);
|
|
64356
|
+
return;
|
|
64357
|
+
}
|
|
64358
|
+
console.log(`Prompt-budget benchmark ${result.status}: ${result.phase}`);
|
|
64359
|
+
console.log(`Run ID: ${result.runId}`);
|
|
64360
|
+
console.log(`Corpus: ${result.corpusHash}`);
|
|
64361
|
+
console.log(`Target model: ${result.targetModel}`);
|
|
64362
|
+
console.log(`Judge: ${result.judgeModel}`);
|
|
64363
|
+
console.log(`Estimated credits: ${result.estimatedCredits.totalCredits}`);
|
|
64364
|
+
console.log(`Spend credits: ${result.spendsCredits ? "yes" : "no"}`);
|
|
64365
|
+
if (result.status !== "planned") {
|
|
64366
|
+
console.log(`Passed cases: ${result.summary.passedCases}/${result.summary.completedCases}`);
|
|
64367
|
+
console.log(`Average turn duration: ${result.summary.averageTurnDurationMs ?? "n/a"}ms`);
|
|
64368
|
+
console.log(`Average judge score: ${result.summary.averageJudgeScore ?? "n/a"}`);
|
|
64369
|
+
for (const benchmarkCase of result.cases) {
|
|
64370
|
+
const mark = benchmarkCase.passed ? "PASS" : "FAIL";
|
|
64371
|
+
const error = benchmarkCase.error ? ` error=${benchmarkCase.error}` : "";
|
|
64372
|
+
console.log(`${mark} ${benchmarkCase.id} (${benchmarkCase.durationMs}ms)${error}`);
|
|
64373
|
+
}
|
|
64374
|
+
}
|
|
64375
|
+
}
|
|
64376
|
+
function writePromptBudgetComparison(result, flags, output) {
|
|
64377
|
+
const json = `${JSON.stringify(result, null, 2)}
|
|
64378
|
+
`;
|
|
64379
|
+
if (output) writeFileSync5(output, json, "utf-8");
|
|
64380
|
+
if (flags.json === true || output) {
|
|
64381
|
+
process.stdout.write(json);
|
|
64382
|
+
return;
|
|
64383
|
+
}
|
|
64384
|
+
console.log(`Prompt-budget comparison ${result.status}: ${result.recommendation}`);
|
|
64385
|
+
console.log(`Corpus: ${result.corpusHash}`);
|
|
64386
|
+
console.log(`Duration delta: ${formatMetricDelta(result.deltas.averageTurnDurationMs, "ms")}`);
|
|
64387
|
+
console.log(`Quality delta: ${formatMetricDelta(result.deltas.averageJudgeScore, "")}`);
|
|
64388
|
+
if (result.regressions.length > 0) {
|
|
64389
|
+
console.log("Regressions:");
|
|
64390
|
+
for (const regression of result.regressions) console.log(`- ${regression}`);
|
|
64391
|
+
}
|
|
64392
|
+
}
|
|
64393
|
+
function comparePromptBudgetArtifacts(flags) {
|
|
64394
|
+
const baselinePath = stringFlag(flags.baseline);
|
|
64395
|
+
const postchangePath = stringFlag(flags.postchange);
|
|
64396
|
+
if (!baselinePath || !postchangePath) {
|
|
64397
|
+
throw new Error("prompt-budget compare requires --baseline <path> and --postchange <path>");
|
|
64398
|
+
}
|
|
64399
|
+
const baseline = readPromptBudgetResult(baselinePath);
|
|
64400
|
+
const postchange = readPromptBudgetResult(postchangePath);
|
|
64401
|
+
if (baseline.command !== "benchmark prompt-budget" || postchange.command !== "benchmark prompt-budget") {
|
|
64402
|
+
throw new Error("prompt-budget compare expects prompt-budget benchmark artifacts");
|
|
64403
|
+
}
|
|
64404
|
+
if (baseline.corpusHash !== postchange.corpusHash) {
|
|
64405
|
+
throw new Error("prompt-budget artifacts use different corpus hashes and cannot be compared");
|
|
64406
|
+
}
|
|
64407
|
+
const regressions = findPromptBudgetRegressions(baseline, postchange);
|
|
64408
|
+
const durationDelta = metricDelta(baseline.summary.averageTurnDurationMs, postchange.summary.averageTurnDurationMs);
|
|
64409
|
+
const qualityDelta = metricDelta(baseline.summary.averageJudgeScore, postchange.summary.averageJudgeScore);
|
|
64410
|
+
const continuityDelta = metricDelta(baseline.summary.averageContinuityScore, postchange.summary.averageContinuityScore);
|
|
64411
|
+
const latencyImproved = durationDelta.delta !== null && durationDelta.delta < 0;
|
|
64412
|
+
const qualityRegressed = regressions.length > 0;
|
|
64413
|
+
return {
|
|
64414
|
+
command: "benchmark prompt-budget compare",
|
|
64415
|
+
status: qualityRegressed ? "needs_fix" : "passed",
|
|
64416
|
+
corpusHash: baseline.corpusHash,
|
|
64417
|
+
baseline: baselinePath,
|
|
64418
|
+
postchange: postchangePath,
|
|
64419
|
+
deltas: {
|
|
64420
|
+
averageTurnDurationMs: durationDelta,
|
|
64421
|
+
averageJudgeScore: qualityDelta,
|
|
64422
|
+
averageContinuityScore: continuityDelta
|
|
64423
|
+
},
|
|
64424
|
+
regressions,
|
|
64425
|
+
recommendation: !qualityRegressed && latencyImproved ? "keep" : "adjust_or_revert"
|
|
64426
|
+
};
|
|
64427
|
+
}
|
|
64428
|
+
function readPromptBudgetResult(path) {
|
|
64429
|
+
const parsed = JSON.parse(readFileSync7(path, "utf-8"));
|
|
64430
|
+
if (!parsed || typeof parsed !== "object") throw new Error(`Invalid prompt-budget artifact: ${path}`);
|
|
64431
|
+
return parsed;
|
|
64432
|
+
}
|
|
64433
|
+
function findPromptBudgetRegressions(baseline, postchange) {
|
|
64434
|
+
const regressions = [];
|
|
64435
|
+
const postCases = new Map(postchange.cases.map((benchmarkCase) => [benchmarkCase.id, benchmarkCase]));
|
|
64436
|
+
for (const baselineCase of baseline.cases) {
|
|
64437
|
+
const postCase = postCases.get(baselineCase.id);
|
|
64438
|
+
if (!postCase) {
|
|
64439
|
+
regressions.push(`${baselineCase.id}: missing post-change case`);
|
|
64440
|
+
continue;
|
|
64441
|
+
}
|
|
64442
|
+
if (baselineCase.promptHashes.join("|") !== postCase.promptHashes.join("|")) {
|
|
64443
|
+
regressions.push(`${baselineCase.id}: prompt hashes differ`);
|
|
64444
|
+
}
|
|
64445
|
+
for (const baselineTurn of baselineCase.turns) {
|
|
64446
|
+
const postTurn = postCase.turns.find((turn) => turn.turn === baselineTurn.turn);
|
|
64447
|
+
if (!postTurn) {
|
|
64448
|
+
regressions.push(`${baselineCase.id} turn ${baselineTurn.turn}: missing post-change turn`);
|
|
64449
|
+
continue;
|
|
64450
|
+
}
|
|
64451
|
+
const baselineScore = baselineTurn.judge?.score;
|
|
64452
|
+
const postScore = postTurn.judge?.score;
|
|
64453
|
+
if (typeof baselineScore === "number" && typeof postScore === "number") {
|
|
64454
|
+
if (baselineScore >= 4 && postScore < 4) {
|
|
64455
|
+
regressions.push(`${baselineCase.id} turn ${baselineTurn.turn}: score dropped below pass threshold (${baselineScore} -> ${postScore})`);
|
|
64456
|
+
} else if (postScore < baselineScore - 1) {
|
|
64457
|
+
regressions.push(`${baselineCase.id} turn ${baselineTurn.turn}: score dropped materially (${baselineScore} -> ${postScore})`);
|
|
64458
|
+
}
|
|
64459
|
+
}
|
|
64460
|
+
}
|
|
64461
|
+
const baselineContinuity = baselineCase.continuityJudge?.score;
|
|
64462
|
+
const postContinuity = postCase.continuityJudge?.score;
|
|
64463
|
+
if (typeof baselineContinuity === "number" && typeof postContinuity === "number") {
|
|
64464
|
+
if (baselineContinuity >= 4 && postContinuity < 4) {
|
|
64465
|
+
regressions.push(`${baselineCase.id}: continuity score dropped below pass threshold (${baselineContinuity} -> ${postContinuity})`);
|
|
64466
|
+
} else if (postContinuity < baselineContinuity - 1) {
|
|
64467
|
+
regressions.push(`${baselineCase.id}: continuity score dropped materially (${baselineContinuity} -> ${postContinuity})`);
|
|
64468
|
+
}
|
|
64469
|
+
}
|
|
64470
|
+
}
|
|
64471
|
+
return regressions;
|
|
64472
|
+
}
|
|
64473
|
+
function metricDelta(baseline, postchange) {
|
|
64474
|
+
if (baseline === null || postchange === null) {
|
|
64475
|
+
return { baseline, postchange, delta: null, percent: null };
|
|
64476
|
+
}
|
|
64477
|
+
const delta = round2(postchange - baseline);
|
|
64478
|
+
const percent = baseline === 0 ? null : round2(delta / baseline * 100);
|
|
64479
|
+
return { baseline, postchange, delta, percent };
|
|
64480
|
+
}
|
|
64481
|
+
function formatMetricDelta(delta, suffix) {
|
|
64482
|
+
if (delta.delta === null) return "n/a";
|
|
64483
|
+
const unit = suffix ? suffix : "";
|
|
64484
|
+
const percent = delta.percent === null ? "" : ` (${delta.percent}%)`;
|
|
64485
|
+
return `${delta.baseline}${unit} -> ${delta.postchange}${unit} (${delta.delta}${unit})${percent}`;
|
|
64486
|
+
}
|
|
63840
64487
|
|
|
63841
64488
|
// src/tui.ts
|
|
63842
64489
|
import { createInterface as createInterface4 } from "readline/promises";
|
|
@@ -66263,7 +66910,7 @@ Starting a new chat...
|
|
|
66263
66910
|
acceptTaskProposals: flags["accept-task-proposals"] === true,
|
|
66264
66911
|
piiDetection: flags["no-pii-detection"] !== true,
|
|
66265
66912
|
taskUpdateJobs: false,
|
|
66266
|
-
responseTimeoutMs:
|
|
66913
|
+
responseTimeoutMs: parseResponseTimeoutMs2(flags),
|
|
66267
66914
|
...teamContextFromFlags(flags)
|
|
66268
66915
|
},
|
|
66269
66916
|
redactor
|
|
@@ -67233,7 +67880,7 @@ async function handleGoalChat(client, flags, redactor) {
|
|
|
67233
67880
|
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
67234
67881
|
acceptTaskProposals: flags["accept-task-proposals"] === true,
|
|
67235
67882
|
piiDetection: flags["no-pii-detection"] !== true,
|
|
67236
|
-
responseTimeoutMs:
|
|
67883
|
+
responseTimeoutMs: parseResponseTimeoutMs2(flags),
|
|
67237
67884
|
...teamContextFromFlags(flags)
|
|
67238
67885
|
},
|
|
67239
67886
|
redactor
|
|
@@ -67816,7 +68463,7 @@ function parsePositiveIntegerFlag(value, flagName) {
|
|
|
67816
68463
|
}
|
|
67817
68464
|
return parsed;
|
|
67818
68465
|
}
|
|
67819
|
-
function
|
|
68466
|
+
function parseResponseTimeoutMs2(flags) {
|
|
67820
68467
|
const seconds = parsePositiveIntegerFlag(flags["response-timeout-seconds"], "--response-timeout-seconds");
|
|
67821
68468
|
return seconds === void 0 ? void 0 : seconds * 1e3;
|
|
67822
68469
|
}
|
|
@@ -68110,7 +68757,7 @@ async function handleChats(client, subcommand, rest, flags, redactor) {
|
|
|
68110
68757
|
send,
|
|
68111
68758
|
dryRun,
|
|
68112
68759
|
confirmDestructive: flags.yes === true,
|
|
68113
|
-
responseTimeoutMs:
|
|
68760
|
+
responseTimeoutMs: parseResponseTimeoutMs2(flags),
|
|
68114
68761
|
...teamContext
|
|
68115
68762
|
});
|
|
68116
68763
|
if (flags.json === true) {
|
|
@@ -68134,7 +68781,7 @@ async function handleChats(client, subcommand, rest, flags, redactor) {
|
|
|
68134
68781
|
chatId,
|
|
68135
68782
|
dryRun,
|
|
68136
68783
|
confirmDestructive: flags.yes === true,
|
|
68137
|
-
responseTimeoutMs:
|
|
68784
|
+
responseTimeoutMs: parseResponseTimeoutMs2(flags),
|
|
68138
68785
|
...teamContext
|
|
68139
68786
|
});
|
|
68140
68787
|
if (flags.json === true) {
|
|
@@ -68161,7 +68808,7 @@ async function handleChats(client, subcommand, rest, flags, redactor) {
|
|
|
68161
68808
|
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
68162
68809
|
acceptTaskProposals: flags["accept-task-proposals"] === true,
|
|
68163
68810
|
piiDetection: flags["no-pii-detection"] !== true,
|
|
68164
|
-
responseTimeoutMs:
|
|
68811
|
+
responseTimeoutMs: parseResponseTimeoutMs2(flags),
|
|
68165
68812
|
...teamContext,
|
|
68166
68813
|
anonymousLearningMode: client.hasSession() ? void 0 : parseAnonymousLearningModeFlags(flags)
|
|
68167
68814
|
},
|
|
@@ -68239,7 +68886,7 @@ Run 'openmates chats show ` + chatId + "' to check if suggestions have been save
|
|
|
68239
68886
|
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
68240
68887
|
acceptTaskProposals: flags["accept-task-proposals"] === true,
|
|
68241
68888
|
piiDetection: flags["no-pii-detection"] !== true,
|
|
68242
|
-
responseTimeoutMs:
|
|
68889
|
+
responseTimeoutMs: parseResponseTimeoutMs2(flags),
|
|
68243
68890
|
...teamContext
|
|
68244
68891
|
},
|
|
68245
68892
|
redactor
|
|
@@ -68270,7 +68917,7 @@ Run 'openmates chats show ` + chatId + "' to check if suggestions have been save
|
|
|
68270
68917
|
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
68271
68918
|
acceptTaskProposals: flags["accept-task-proposals"] === true,
|
|
68272
68919
|
piiDetection: flags["no-pii-detection"] !== true,
|
|
68273
|
-
responseTimeoutMs:
|
|
68920
|
+
responseTimeoutMs: parseResponseTimeoutMs2(flags),
|
|
68274
68921
|
...teamContext
|
|
68275
68922
|
},
|
|
68276
68923
|
redactor
|
|
@@ -68293,7 +68940,7 @@ Run 'openmates chats show ` + chatId + "' to check if suggestions have been save
|
|
|
68293
68940
|
autoApproveSubChats: flags["auto-approve"] === true,
|
|
68294
68941
|
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
68295
68942
|
piiDetection: flags["no-pii-detection"] !== true,
|
|
68296
|
-
responseTimeoutMs:
|
|
68943
|
+
responseTimeoutMs: parseResponseTimeoutMs2(flags)
|
|
68297
68944
|
},
|
|
68298
68945
|
redactor
|
|
68299
68946
|
);
|
|
@@ -68856,7 +69503,7 @@ async function sendApiKeyChatNew(client, apiKey, message, flags) {
|
|
|
68856
69503
|
try {
|
|
68857
69504
|
response = await sdk.chats.send(message, {
|
|
68858
69505
|
saveToAccount: false,
|
|
68859
|
-
recoveryTimeoutMs:
|
|
69506
|
+
recoveryTimeoutMs: parseResponseTimeoutMs2(flags)
|
|
68860
69507
|
});
|
|
68861
69508
|
} catch (err) {
|
|
68862
69509
|
if (!isSdkChatScopeDenied(err)) throw err;
|
|
@@ -68998,7 +69645,7 @@ function normalizeApiKeyLoadedChat(payload) {
|
|
|
68998
69645
|
const messages = rawMessages.map((message) => {
|
|
68999
69646
|
const messageId = String(message.client_message_id ?? message.message_id ?? message.id ?? "");
|
|
69000
69647
|
const directEmbedIds = Array.isArray(message.embedIds) ? message.embedIds : Array.isArray(message.embed_ids) ? message.embed_ids : [];
|
|
69001
|
-
const hashedMessageId = messageId ?
|
|
69648
|
+
const hashedMessageId = messageId ? createHash14("sha256").update(messageId).digest("hex") : null;
|
|
69002
69649
|
const matchedEmbedIds = hashedMessageId ? rawEmbeds.filter((embed) => embed.hashed_message_id === hashedMessageId && !embed.parent_embed_id).map((embed) => String(embed.embed_id ?? embed.id ?? "")).filter(Boolean) : [];
|
|
69003
69650
|
return {
|
|
69004
69651
|
id: messageId,
|
|
@@ -69658,9 +70305,9 @@ Run: openmates apps ${command.app_id} ${command.skill_id} --help`
|
|
|
69658
70305
|
return value;
|
|
69659
70306
|
}
|
|
69660
70307
|
function buildTravelConnectionsRequest(positionals, flags) {
|
|
69661
|
-
const origin =
|
|
69662
|
-
const destination =
|
|
69663
|
-
const date =
|
|
70308
|
+
const origin = stringFlag2(flags, "origin") ?? positionals[0];
|
|
70309
|
+
const destination = stringFlag2(flags, "destination") ?? positionals[1];
|
|
70310
|
+
const date = stringFlag2(flags, "date") ?? positionals[2];
|
|
69664
70311
|
if (!origin || !destination || !date) {
|
|
69665
70312
|
throw new Error(
|
|
69666
70313
|
"Missing travel route. Use --origin <place> --destination <place> --date <YYYY-MM-DD>.\n\nExample: openmates apps travel search_connections --origin Berlin --destination Munich --date 2026-08-01 --json"
|
|
@@ -69669,12 +70316,12 @@ function buildTravelConnectionsRequest(positionals, flags) {
|
|
|
69669
70316
|
const request = {
|
|
69670
70317
|
legs: [{ origin, destination, date }]
|
|
69671
70318
|
};
|
|
69672
|
-
const transport =
|
|
70319
|
+
const transport = stringFlag2(flags, "transport") ?? stringFlag2(flags, "transport-method");
|
|
69673
70320
|
if (transport) request.transport_methods = [transport];
|
|
69674
70321
|
return request;
|
|
69675
70322
|
}
|
|
69676
70323
|
function buildCodeImageToHtmlRequest(positionals, flags) {
|
|
69677
|
-
const file =
|
|
70324
|
+
const file = stringFlag2(flags, "file") ?? positionals[0];
|
|
69678
70325
|
if (!file) {
|
|
69679
70326
|
throw new Error(
|
|
69680
70327
|
"Missing screenshot file. Use --file <path>.\n\nExample: openmates apps code image_to_html --file ./mockup.png --json"
|
|
@@ -69722,7 +70369,7 @@ function primaryPositionalProperty(properties, required) {
|
|
|
69722
70369
|
function readFlag(flags, name) {
|
|
69723
70370
|
return flags[name] ?? flags[kebabCase(name)];
|
|
69724
70371
|
}
|
|
69725
|
-
function
|
|
70372
|
+
function stringFlag2(flags, name) {
|
|
69726
70373
|
const raw = readFlag(flags, name);
|
|
69727
70374
|
return typeof raw === "string" && raw.trim() ? raw.trim() : void 0;
|
|
69728
70375
|
}
|
|
@@ -70078,7 +70725,7 @@ async function handleDesignIconExport(client, positionals, flags, apiKey) {
|
|
|
70078
70725
|
printDesignIconExportHelp();
|
|
70079
70726
|
return;
|
|
70080
70727
|
}
|
|
70081
|
-
const outputPath =
|
|
70728
|
+
const outputPath = stringFlag2(flags, "output");
|
|
70082
70729
|
if (!outputPath) {
|
|
70083
70730
|
console.error(
|
|
70084
70731
|
"Missing --output flag.\n\nUsage:\n openmates apps design export-icon lucide:home --output home.svg [--color '#111827']\n openmates apps design export-icon --prefix lucide --name home --format png --size 64 --output home.png\n"
|
|
@@ -70095,7 +70742,7 @@ async function handleDesignIconExport(client, positionals, flags, apiKey) {
|
|
|
70095
70742
|
...iconRef,
|
|
70096
70743
|
outputPath,
|
|
70097
70744
|
format,
|
|
70098
|
-
color:
|
|
70745
|
+
color: stringFlag2(flags, "color"),
|
|
70099
70746
|
palette: flags.palette === true,
|
|
70100
70747
|
allowPaletteRecolor: flags["allow-palette-recolor"] === true,
|
|
70101
70748
|
size,
|
|
@@ -70122,9 +70769,9 @@ async function handleDesignIconExport(client, positionals, flags, apiKey) {
|
|
|
70122
70769
|
}
|
|
70123
70770
|
}
|
|
70124
70771
|
function parseDesignIconReference(positionals, flags) {
|
|
70125
|
-
const svgPath =
|
|
70772
|
+
const svgPath = stringFlag2(flags, "svg-path");
|
|
70126
70773
|
if (svgPath) return { svgPath };
|
|
70127
|
-
const icon =
|
|
70774
|
+
const icon = stringFlag2(flags, "icon") ?? positionals[0];
|
|
70128
70775
|
if (icon) {
|
|
70129
70776
|
const [prefix, name, extra] = icon.split(":");
|
|
70130
70777
|
if (!prefix || !name || extra !== void 0) {
|
|
@@ -70133,8 +70780,8 @@ function parseDesignIconReference(positionals, flags) {
|
|
|
70133
70780
|
return { prefix, name };
|
|
70134
70781
|
}
|
|
70135
70782
|
return {
|
|
70136
|
-
prefix:
|
|
70137
|
-
name:
|
|
70783
|
+
prefix: stringFlag2(flags, "prefix"),
|
|
70784
|
+
name: stringFlag2(flags, "name")
|
|
70138
70785
|
};
|
|
70139
70786
|
}
|
|
70140
70787
|
function parseDesignIconExportFormat(value) {
|
|
@@ -71172,12 +71819,12 @@ async function handleFinance(client, subcommand, _rest, flags) {
|
|
|
71172
71819
|
if (flags["csv-only"] !== true) {
|
|
71173
71820
|
try {
|
|
71174
71821
|
connectedAccount = await client.decryptConnectedAccountForSkill({
|
|
71175
|
-
accountId:
|
|
71822
|
+
accountId: stringFlag2(flags, "connected-account"),
|
|
71176
71823
|
appId: "finance",
|
|
71177
71824
|
providerId: "revolut_business"
|
|
71178
71825
|
});
|
|
71179
71826
|
} catch (error) {
|
|
71180
|
-
if (csvStatements.length === 0 ||
|
|
71827
|
+
if (csvStatements.length === 0 || stringFlag2(flags, "connected-account")) throw error;
|
|
71181
71828
|
}
|
|
71182
71829
|
}
|
|
71183
71830
|
if (!connectedAccount && csvStatements.length === 0) {
|
|
@@ -71215,11 +71862,11 @@ function readFinanceCsvStatements(flags) {
|
|
|
71215
71862
|
}
|
|
71216
71863
|
function buildFinanceCheckAccountsInput(flags, csvStatements, connectedAccount) {
|
|
71217
71864
|
const input = {
|
|
71218
|
-
period:
|
|
71219
|
-
projection_horizon:
|
|
71865
|
+
period: stringFlag2(flags, "period") ?? "monthly",
|
|
71866
|
+
projection_horizon: stringFlag2(flags, "projection-horizon") ?? "monthly"
|
|
71220
71867
|
};
|
|
71221
|
-
const startDate =
|
|
71222
|
-
const endDate =
|
|
71868
|
+
const startDate = stringFlag2(flags, "from") ?? stringFlag2(flags, "start-date");
|
|
71869
|
+
const endDate = stringFlag2(flags, "to") ?? stringFlag2(flags, "end-date");
|
|
71223
71870
|
if (startDate) input.start_date = startDate;
|
|
71224
71871
|
if (endDate) input.end_date = endDate;
|
|
71225
71872
|
addFinanceListFilter(input, "account_filters", flags.account);
|
|
@@ -71227,7 +71874,7 @@ function buildFinanceCheckAccountsInput(flags, csvStatements, connectedAccount)
|
|
|
71227
71874
|
addFinanceListFilter(input, "category_filters", flags.category ?? flags.categories);
|
|
71228
71875
|
addFinanceListFilter(input, "state_filters", flags.state ?? flags.states);
|
|
71229
71876
|
addFinanceListFilter(input, "placeholder_filters", flags.placeholder ?? flags.placeholders);
|
|
71230
|
-
const direction =
|
|
71877
|
+
const direction = stringFlag2(flags, "direction");
|
|
71231
71878
|
if (direction) input.direction_filter = direction;
|
|
71232
71879
|
if (csvStatements.length > 0) input.csv_statements = csvStatements;
|
|
71233
71880
|
if (connectedAccount) {
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -80,6 +80,16 @@ interface StreamEvent {
|
|
|
80
80
|
/** Human-readable model name. */
|
|
81
81
|
modelName: string | null;
|
|
82
82
|
}
|
|
83
|
+
type AiResponseTokenUsage = {
|
|
84
|
+
promptTokens?: number;
|
|
85
|
+
completionTokens?: number;
|
|
86
|
+
userInputTokens?: number;
|
|
87
|
+
systemPromptTokens?: number;
|
|
88
|
+
totalCredits?: number;
|
|
89
|
+
};
|
|
90
|
+
type AiResponsePromptBudget = {
|
|
91
|
+
systemPromptTokens?: number;
|
|
92
|
+
};
|
|
83
93
|
interface ChatCompressionCheckpointEvent {
|
|
84
94
|
chatId: string;
|
|
85
95
|
taskId: string | null;
|
|
@@ -242,6 +252,8 @@ declare class OpenMatesWsClient {
|
|
|
242
252
|
subChatEvents: SubChatEvent[];
|
|
243
253
|
recoveryJobId: string | null;
|
|
244
254
|
compressionCheckpoints: ChatCompressionCheckpointEvent[];
|
|
255
|
+
tokenUsage: AiResponseTokenUsage | null;
|
|
256
|
+
promptBudget: AiResponsePromptBudget | null;
|
|
245
257
|
}>;
|
|
246
258
|
}
|
|
247
259
|
|
|
@@ -1634,6 +1646,10 @@ declare class OpenMatesClient {
|
|
|
1634
1646
|
approvedKeys: string[];
|
|
1635
1647
|
entryCount: number;
|
|
1636
1648
|
}>;
|
|
1649
|
+
/** Final-frame token usage surfaced by the backend when provider usage metadata is available. */
|
|
1650
|
+
tokenUsage: AiResponseTokenUsage | null;
|
|
1651
|
+
/** Prompt-budget metrics surfaced by the backend when available. */
|
|
1652
|
+
promptBudget: AiResponsePromptBudget | null;
|
|
1637
1653
|
}>;
|
|
1638
1654
|
createTurnTokenRefs(params: {
|
|
1639
1655
|
chatId: string;
|
|
@@ -1985,6 +2001,10 @@ declare class OpenMatesClient {
|
|
|
1985
2001
|
approvedKeys: string[];
|
|
1986
2002
|
entryCount: number;
|
|
1987
2003
|
}>;
|
|
2004
|
+
/** Final-frame token usage surfaced by the backend when provider usage metadata is available. */
|
|
2005
|
+
tokenUsage: AiResponseTokenUsage | null;
|
|
2006
|
+
/** Prompt-budget metrics surfaced by the backend when available. */
|
|
2007
|
+
promptBudget: AiResponsePromptBudget | null;
|
|
1988
2008
|
}>;
|
|
1989
2009
|
private persistEncryptedSystemMessage;
|
|
1990
2010
|
private persistPendingTaskUpdateJobs;
|
package/dist/index.js
CHANGED