@usabledev/usable-chat 1.149.0 → 1.150.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/cli.js +149 -5
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -12309,9 +12309,9 @@ var init_markdown = __esm({
|
|
|
12309
12309
|
const startNumber = typeof token.start === "number" ? token.start : 1;
|
|
12310
12310
|
for (let i18 = 0; i18 < token.items.length; i18++) {
|
|
12311
12311
|
const item = token.items[i18];
|
|
12312
|
-
const
|
|
12312
|
+
const bullet3 = token.ordered ? this.options.preserveOrderedListMarkers ? this.getOrderedListMarker(item) ?? `${startNumber + i18}. ` : `${startNumber + i18}. ` : "- ";
|
|
12313
12313
|
const taskMarker = item.task ? `[${item.checked ? "x" : " "}] ` : "";
|
|
12314
|
-
const marker15 =
|
|
12314
|
+
const marker15 = bullet3 + taskMarker;
|
|
12315
12315
|
const firstPrefix = indent + this.theme.listBullet(marker15);
|
|
12316
12316
|
const continuationPrefix = indent + " ".repeat(visibleWidth(marker15));
|
|
12317
12317
|
const itemWidth = Math.max(1, width - visibleWidth(firstPrefix));
|
|
@@ -23746,6 +23746,109 @@ Usable is the user's long-term memory. When a task touches project domain, past
|
|
|
23746
23746
|
}
|
|
23747
23747
|
});
|
|
23748
23748
|
|
|
23749
|
+
// src/core/orchestrator/harness-compaction.ts
|
|
23750
|
+
function toolCallsOf(msg) {
|
|
23751
|
+
const out = [];
|
|
23752
|
+
const m33 = msg;
|
|
23753
|
+
if (Array.isArray(m33.tool_calls)) {
|
|
23754
|
+
for (const tc of m33.tool_calls) {
|
|
23755
|
+
const fn4 = tc.function ?? tc;
|
|
23756
|
+
const name14 = String(fn4.name ?? tc.name ?? "");
|
|
23757
|
+
let args = {};
|
|
23758
|
+
const raw = fn4.arguments ?? tc.args ?? tc.input;
|
|
23759
|
+
if (typeof raw === "string") {
|
|
23760
|
+
try {
|
|
23761
|
+
args = JSON.parse(raw);
|
|
23762
|
+
} catch {
|
|
23763
|
+
args = {};
|
|
23764
|
+
}
|
|
23765
|
+
} else if (raw && typeof raw === "object") {
|
|
23766
|
+
args = raw;
|
|
23767
|
+
}
|
|
23768
|
+
if (name14) out.push({ name: name14, args });
|
|
23769
|
+
}
|
|
23770
|
+
}
|
|
23771
|
+
if (Array.isArray(msg.content)) {
|
|
23772
|
+
for (const part of msg.content) {
|
|
23773
|
+
const type = part.type;
|
|
23774
|
+
if (type === "tool_use" || type === "tool-call") {
|
|
23775
|
+
const name14 = String(part.name ?? part.toolName ?? "");
|
|
23776
|
+
const args = part.input ?? part.args ?? {};
|
|
23777
|
+
if (name14) out.push({ name: name14, args });
|
|
23778
|
+
}
|
|
23779
|
+
}
|
|
23780
|
+
}
|
|
23781
|
+
return out;
|
|
23782
|
+
}
|
|
23783
|
+
function extractFileOps(messages4) {
|
|
23784
|
+
const read = /* @__PURE__ */ new Set();
|
|
23785
|
+
const modified = /* @__PURE__ */ new Set();
|
|
23786
|
+
for (const msg of messages4) {
|
|
23787
|
+
if (msg.role !== "assistant") continue;
|
|
23788
|
+
for (const { name: name14, args } of toolCallsOf(msg)) {
|
|
23789
|
+
const path11 = args.path ?? args.file ?? args.pattern;
|
|
23790
|
+
if (!path11 || typeof path11 !== "string") continue;
|
|
23791
|
+
if (WRITE_TOOLS.has(name14)) modified.add(path11);
|
|
23792
|
+
else if (READ_TOOLS.has(name14)) read.add(path11);
|
|
23793
|
+
}
|
|
23794
|
+
}
|
|
23795
|
+
return { read: [...read], modified: [...modified] };
|
|
23796
|
+
}
|
|
23797
|
+
function isStepStart(messages4, i18) {
|
|
23798
|
+
if (i18 <= 0) return false;
|
|
23799
|
+
return messages4[i18].role === "assistant" && messages4[i18 - 1].role !== "assistant";
|
|
23800
|
+
}
|
|
23801
|
+
function findCutIndex(messages4, keepRecentTokens, countTokens2) {
|
|
23802
|
+
let sum2 = 0;
|
|
23803
|
+
for (let i18 = messages4.length - 1; i18 >= 1; i18--) {
|
|
23804
|
+
sum2 += countTokens2(messages4[i18]);
|
|
23805
|
+
if (sum2 >= keepRecentTokens && isStepStart(messages4, i18)) return i18;
|
|
23806
|
+
}
|
|
23807
|
+
return 0;
|
|
23808
|
+
}
|
|
23809
|
+
function bullet2(label, items) {
|
|
23810
|
+
if (items.length === 0) return "";
|
|
23811
|
+
const shown = items.slice(0, 30);
|
|
23812
|
+
const more = items.length > shown.length ? ` (+${items.length - shown.length} more)` : "";
|
|
23813
|
+
return `
|
|
23814
|
+
${label}: ${shown.join(", ")}${more}`;
|
|
23815
|
+
}
|
|
23816
|
+
function goalText(messages4) {
|
|
23817
|
+
const first = messages4[0];
|
|
23818
|
+
if (!first || first.role !== "user") return "";
|
|
23819
|
+
if (typeof first.content === "string") return first.content;
|
|
23820
|
+
if (Array.isArray(first.content)) {
|
|
23821
|
+
const text2 = first.content.filter((p28) => p28.type === "text" && typeof p28.text === "string").map((p28) => p28.text).join("\n");
|
|
23822
|
+
return text2;
|
|
23823
|
+
}
|
|
23824
|
+
return "";
|
|
23825
|
+
}
|
|
23826
|
+
function buildCompactionMarker(goal, droppedCount, fileOps) {
|
|
23827
|
+
const text2 = (goal ? `Original task:
|
|
23828
|
+
${goal}
|
|
23829
|
+
|
|
23830
|
+
` : "") + `[context-compacted] ${droppedCount} earlier messages were pruned to free the context window; the original task above and the most recent steps are preserved.` + bullet2("Files read so far", fileOps.read) + bullet2("Files modified so far", fileOps.modified) + `
|
|
23831
|
+
Continue the task from where it left off \u2014 do NOT restart it. Re-read a file with the read tool if you need its current contents.`;
|
|
23832
|
+
return { role: "user", content: text2 };
|
|
23833
|
+
}
|
|
23834
|
+
function compactHarnessConversation(messages4, opts) {
|
|
23835
|
+
const minMessages = opts.minMessages ?? 8;
|
|
23836
|
+
if (messages4.length < minMessages) return { messages: messages4, compacted: false, droppedCount: 0 };
|
|
23837
|
+
const cut = findCutIndex(messages4, opts.keepRecentTokens, opts.countTokens);
|
|
23838
|
+
if (cut < 3) return { messages: messages4, compacted: false, droppedCount: 0 };
|
|
23839
|
+
const dropped = messages4.slice(0, cut);
|
|
23840
|
+
const marker15 = buildCompactionMarker(goalText(messages4), dropped.length, extractFileOps(dropped));
|
|
23841
|
+
return { messages: [marker15, ...messages4.slice(cut)], compacted: true, droppedCount: dropped.length };
|
|
23842
|
+
}
|
|
23843
|
+
var READ_TOOLS, WRITE_TOOLS;
|
|
23844
|
+
var init_harness_compaction = __esm({
|
|
23845
|
+
"src/core/orchestrator/harness-compaction.ts"() {
|
|
23846
|
+
"use strict";
|
|
23847
|
+
READ_TOOLS = /* @__PURE__ */ new Set(["read", "glob", "grep"]);
|
|
23848
|
+
WRITE_TOOLS = /* @__PURE__ */ new Set(["write", "edit"]);
|
|
23849
|
+
}
|
|
23850
|
+
});
|
|
23851
|
+
|
|
23749
23852
|
// src/core/orchestrator/ask-user-question-prompt.ts
|
|
23750
23853
|
var ASK_USER_QUESTION_PROMPT;
|
|
23751
23854
|
var init_ask_user_question_prompt = __esm({
|
|
@@ -242907,6 +243010,37 @@ ${combinedSystemMessage}` : combinedSystemMessage;
|
|
|
242907
243010
|
name: msg.name
|
|
242908
243011
|
};
|
|
242909
243012
|
};
|
|
243013
|
+
if (config3.localFilesystem) {
|
|
243014
|
+
const harnessCtxLen = getModelById(selectedModelId)?.capabilities.contextLength || 128e3;
|
|
243015
|
+
const envRatio = Number(process.env.USABLE_HARNESS_COMPACT_RATIO);
|
|
243016
|
+
const compactRatio = envRatio > 0 && envRatio <= 1 ? envRatio : HARNESS_COMPACT_RATIO;
|
|
243017
|
+
const tokensOf = (m33) => countTokens(typeof m33.content === "string" ? m33.content : JSON.stringify(m33.content));
|
|
243018
|
+
const estTokens = conversationMessages.reduce((s17, m33) => s17 + tokensOf(m33), 0);
|
|
243019
|
+
if (estTokens > harnessCtxLen * compactRatio) {
|
|
243020
|
+
const beforeLen = conversationMessages.length;
|
|
243021
|
+
const envKeep = Number(process.env.USABLE_HARNESS_KEEP_TOKENS);
|
|
243022
|
+
const keepRecentTokens = envKeep > 0 ? envKeep : Math.floor(harnessCtxLen * HARNESS_KEEP_RECENT_RATIO);
|
|
243023
|
+
const res = compactHarnessConversation(conversationMessages, {
|
|
243024
|
+
keepRecentTokens,
|
|
243025
|
+
countTokens: tokensOf
|
|
243026
|
+
});
|
|
243027
|
+
if (res.compacted) {
|
|
243028
|
+
conversationMessages = ensureToolCallIntegrity(res.messages);
|
|
243029
|
+
orchestrationLogger.warn("\u{1F9F9} Harness proactive compaction", {
|
|
243030
|
+
beforeLen,
|
|
243031
|
+
afterLen: conversationMessages.length,
|
|
243032
|
+
droppedCount: res.droppedCount,
|
|
243033
|
+
estTokens,
|
|
243034
|
+
contextLength: harnessCtxLen
|
|
243035
|
+
});
|
|
243036
|
+
const compactPlan = emitter.emit("plan", {
|
|
243037
|
+
plan: `\u{1F9F9} Compacted ${res.droppedCount} older messages to free context \u2014 continuing.`,
|
|
243038
|
+
steps: config3.maxSteps
|
|
243039
|
+
});
|
|
243040
|
+
multiplexer.send(compactPlan);
|
|
243041
|
+
}
|
|
243042
|
+
}
|
|
243043
|
+
}
|
|
242910
243044
|
const filteredConversationMessages = filterImagesFromHistory(conversationMessages, {
|
|
242911
243045
|
preserveLastUserMessageImages: !!modelInfo?.capabilities?.vision
|
|
242912
243046
|
});
|
|
@@ -243096,7 +243230,7 @@ ${combinedSystemMessage}` : combinedSystemMessage;
|
|
|
243096
243230
|
});
|
|
243097
243231
|
multiplexer.send(compactionEvent);
|
|
243098
243232
|
}
|
|
243099
|
-
if (contextUsagePercent >= 85 && !forceNoTools) {
|
|
243233
|
+
if (contextUsagePercent >= 85 && !forceNoTools && !config3.localFilesystem) {
|
|
243100
243234
|
forceNoTools = true;
|
|
243101
243235
|
orchestrationLogger.warn("\u{1F6D1} Context at 85%+ - forcing synthesis, disabling tools", {
|
|
243102
243236
|
contextUsagePercent: Math.round(contextUsagePercent)
|
|
@@ -243857,7 +243991,7 @@ function createOrchestratorRequest(messages4, context, config3, persona, apiKey,
|
|
|
243857
243991
|
// Pass MCP tools through
|
|
243858
243992
|
};
|
|
243859
243993
|
}
|
|
243860
|
-
var HARD_TOOL_RESULT_CEILING_CHARS, ASK_QUESTION_PLACEHOLDER;
|
|
243994
|
+
var HARNESS_COMPACT_RATIO, HARNESS_KEEP_RECENT_RATIO, HARD_TOOL_RESULT_CEILING_CHARS, ASK_QUESTION_PLACEHOLDER;
|
|
243861
243995
|
var init_orchestrator = __esm({
|
|
243862
243996
|
"src/core/orchestrator/index.ts"() {
|
|
243863
243997
|
"use strict";
|
|
@@ -243868,6 +244002,7 @@ var init_orchestrator = __esm({
|
|
|
243868
244002
|
init_config();
|
|
243869
244003
|
init_code_execution_prompt();
|
|
243870
244004
|
init_harness_prompt();
|
|
244005
|
+
init_harness_compaction();
|
|
243871
244006
|
init_ask_user_question_prompt();
|
|
243872
244007
|
init_artifact_mention_resolver();
|
|
243873
244008
|
init_slack_mention_resolver();
|
|
@@ -243903,6 +244038,8 @@ var init_orchestrator = __esm({
|
|
|
243903
244038
|
init_mcp_server_tools_prompt();
|
|
243904
244039
|
init_spill_payload();
|
|
243905
244040
|
init_token_counter();
|
|
244041
|
+
HARNESS_COMPACT_RATIO = 0.75;
|
|
244042
|
+
HARNESS_KEEP_RECENT_RATIO = 0.35;
|
|
243906
244043
|
registerAllExperts();
|
|
243907
244044
|
HARD_TOOL_RESULT_CEILING_CHARS = 4e4;
|
|
243908
244045
|
ASK_QUESTION_PLACEHOLDER = "[Awaiting user answer \u2014 picker is open]";
|
|
@@ -261445,6 +261582,13 @@ Edit it, then /verify-extension ${arg.trim()} to check it loads, /trust (project
|
|
|
261445
261582
|
(it7) => it7.kind === "tool" && it7.id === data2.id ? { ...it7, status: data2.success === false ? "error" : "done", result, error: error41 } : it7
|
|
261446
261583
|
);
|
|
261447
261584
|
ui2.requestRender();
|
|
261585
|
+
} else if (e14.type === "plan") {
|
|
261586
|
+
const plan = e14.data?.plan;
|
|
261587
|
+
if (plan && plan.startsWith("\u{1F9F9}")) {
|
|
261588
|
+
finalize();
|
|
261589
|
+
push({ kind: "system", text: plan, tone: "info" });
|
|
261590
|
+
ui2.requestRender();
|
|
261591
|
+
}
|
|
261448
261592
|
}
|
|
261449
261593
|
};
|
|
261450
261594
|
try {
|
|
@@ -262279,7 +262423,7 @@ init_tui_select();
|
|
|
262279
262423
|
init_model_registry();
|
|
262280
262424
|
|
|
262281
262425
|
// package.json
|
|
262282
|
-
var version = "1.
|
|
262426
|
+
var version = "1.150.0";
|
|
262283
262427
|
|
|
262284
262428
|
// src/adapters/cli/model-catalog.ts
|
|
262285
262429
|
init_codex_auth();
|