@charzhu/openjaw-agent 0.3.0 → 0.3.2
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/README.md +10 -0
- package/config.yaml +15 -0
- package/dist/main.js +1725 -356
- package/dist/main.js.map +4 -4
- package/package.json +1 -1
- package/prompts/IDENTITY.md +2 -2
- package/prompts/REASONING.md +17 -1
package/dist/main.js
CHANGED
|
@@ -131,13 +131,18 @@ function loadAgentConfig() {
|
|
|
131
131
|
copilot_oauth_client_id: parsedLlm?.copilot_oauth_client_id ?? DEFAULT_CONFIG.llm.copilot_oauth_client_id,
|
|
132
132
|
context_compression: parsedLlm?.context_compression,
|
|
133
133
|
compression_threshold: parsedLlm?.compression_threshold,
|
|
134
|
-
compression_model: parsedLlm?.compression_model
|
|
134
|
+
compression_model: parsedLlm?.compression_model,
|
|
135
|
+
max_tool_rounds: parsedLlm?.max_tool_rounds
|
|
135
136
|
},
|
|
136
137
|
telegram: parsed?.telegram ?? void 0,
|
|
137
138
|
feishu: parsed?.feishu ?? void 0,
|
|
138
139
|
wechat: parsed?.wechat ?? void 0,
|
|
139
140
|
features: {
|
|
140
|
-
skill_auto_suggest: parsed?.features?.skill_auto_suggest ?? true
|
|
141
|
+
skill_auto_suggest: parsed?.features?.skill_auto_suggest ?? true,
|
|
142
|
+
dynamic_workflows: {
|
|
143
|
+
...DEFAULT_DYNAMIC_WORKFLOWS,
|
|
144
|
+
...parsed?.features?.dynamic_workflows ?? {}
|
|
145
|
+
}
|
|
141
146
|
}
|
|
142
147
|
};
|
|
143
148
|
if (config.llm.provider === "anthropic") {
|
|
@@ -199,12 +204,20 @@ function updateBridgeConfig(name, values) {
|
|
|
199
204
|
);
|
|
200
205
|
return next;
|
|
201
206
|
}
|
|
202
|
-
var DEFAULT_COPILOT_OAUTH_CLIENT_ID, DEFAULT_CONFIG, configWriteChain;
|
|
207
|
+
var DEFAULT_COPILOT_OAUTH_CLIENT_ID, DEFAULT_DYNAMIC_WORKFLOWS, DEFAULT_CONFIG, configWriteChain;
|
|
203
208
|
var init_config = __esm({
|
|
204
209
|
"src/config.ts"() {
|
|
205
210
|
"use strict";
|
|
206
211
|
init_packageRoot();
|
|
207
212
|
DEFAULT_COPILOT_OAUTH_CLIENT_ID = "Iv1.b507a08c87ecfe98";
|
|
213
|
+
DEFAULT_DYNAMIC_WORKFLOWS = {
|
|
214
|
+
enabled: true,
|
|
215
|
+
planner_mode: "adaptive",
|
|
216
|
+
hard_max_workers: 1024,
|
|
217
|
+
hard_max_concurrent_workers: 128,
|
|
218
|
+
worker_timeout_ms: 18e4,
|
|
219
|
+
persist_history: true
|
|
220
|
+
};
|
|
208
221
|
DEFAULT_CONFIG = {
|
|
209
222
|
llm: {
|
|
210
223
|
provider: "anthropic",
|
|
@@ -856,7 +869,7 @@ Start-Sleep -Milliseconds 50
|
|
|
856
869
|
}
|
|
857
870
|
}
|
|
858
871
|
async function wait(duration) {
|
|
859
|
-
await new Promise((
|
|
872
|
+
await new Promise((resolve6) => setTimeout(resolve6, duration * 1e3));
|
|
860
873
|
return { output: `Waited ${duration} seconds` };
|
|
861
874
|
}
|
|
862
875
|
function getDisplayDimensions() {
|
|
@@ -2551,7 +2564,7 @@ var init_copilot = __esm({
|
|
|
2551
2564
|
handshakeTimeout: RESPONSES_WEBSOCKET_CONNECT_TIMEOUT_MS
|
|
2552
2565
|
});
|
|
2553
2566
|
const request = JSON.stringify(this.buildResponsesWebSocketRequest(requestBody));
|
|
2554
|
-
return new Promise((
|
|
2567
|
+
return new Promise((resolve6, reject) => {
|
|
2555
2568
|
const accumulator = { sawTextDelta: false, text: null, toolCalls: [] };
|
|
2556
2569
|
let settled = false;
|
|
2557
2570
|
const timeout = setTimeout(() => {
|
|
@@ -2565,7 +2578,7 @@ var init_copilot = __esm({
|
|
|
2565
2578
|
settled = true;
|
|
2566
2579
|
clearTimeout(timeout);
|
|
2567
2580
|
ws.close();
|
|
2568
|
-
|
|
2581
|
+
resolve6(value);
|
|
2569
2582
|
}, "finish");
|
|
2570
2583
|
const fail = /* @__PURE__ */ __name((error) => {
|
|
2571
2584
|
if (settled) return;
|
|
@@ -2629,7 +2642,8 @@ var init_copilot = __esm({
|
|
|
2629
2642
|
messages.push({
|
|
2630
2643
|
role: "assistant",
|
|
2631
2644
|
content: msg.content,
|
|
2632
|
-
...toolCalls?.length ? { tool_calls: toolCalls } : {}
|
|
2645
|
+
...toolCalls?.length ? { tool_calls: toolCalls } : {},
|
|
2646
|
+
...msg.reasoningOpaque ? { reasoning_opaque: msg.reasoningOpaque } : {}
|
|
2633
2647
|
});
|
|
2634
2648
|
} else {
|
|
2635
2649
|
for (const result of msg.results) {
|
|
@@ -2659,11 +2673,12 @@ var init_copilot = __esm({
|
|
|
2659
2673
|
messages: this.buildChatMessages(options),
|
|
2660
2674
|
tools: options.tools.length > 0 ? options.tools.map(toChatTool) : void 0,
|
|
2661
2675
|
tool_choice: options.tools.length > 0 ? "auto" : void 0,
|
|
2662
|
-
temperature: this.config.temperature
|
|
2676
|
+
temperature: this.config.temperature,
|
|
2677
|
+
stream: true
|
|
2663
2678
|
};
|
|
2664
2679
|
const res = await fetch(`${await this.baseUrl(options.signal)}/chat/completions`, {
|
|
2665
2680
|
method: "POST",
|
|
2666
|
-
headers: await this.headers(options),
|
|
2681
|
+
headers: { ...await this.headers(options), Accept: "text/event-stream" },
|
|
2667
2682
|
body: JSON.stringify(requestBody),
|
|
2668
2683
|
signal: options.signal
|
|
2669
2684
|
});
|
|
@@ -2671,19 +2686,85 @@ var init_copilot = __esm({
|
|
|
2671
2686
|
const detail = await res.text();
|
|
2672
2687
|
throw new Error(`GitHub Copilot chat error: ${res.status} ${detail}`);
|
|
2673
2688
|
}
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2689
|
+
if (!res.body) throw new Error("GitHub Copilot chat: no response body for stream");
|
|
2690
|
+
let text = null;
|
|
2691
|
+
let finishReason;
|
|
2692
|
+
let reasoningOpaque;
|
|
2693
|
+
let promptTokens;
|
|
2694
|
+
let completionTokens;
|
|
2695
|
+
const toolSlots = [];
|
|
2696
|
+
const lastSlotByIndex = /* @__PURE__ */ new Map();
|
|
2697
|
+
const reader = res.body.getReader();
|
|
2698
|
+
const decoder = new TextDecoder();
|
|
2699
|
+
let buf = "";
|
|
2700
|
+
for (; ; ) {
|
|
2701
|
+
const { done, value } = await reader.read();
|
|
2702
|
+
if (done) break;
|
|
2703
|
+
buf += decoder.decode(value, { stream: true });
|
|
2704
|
+
let nl;
|
|
2705
|
+
while ((nl = buf.indexOf("\n")) !== -1) {
|
|
2706
|
+
const rawLine = buf.slice(0, nl).trim();
|
|
2707
|
+
buf = buf.slice(nl + 1);
|
|
2708
|
+
if (!rawLine.startsWith("data:")) continue;
|
|
2709
|
+
const payload = rawLine.slice(5).trim();
|
|
2710
|
+
if (payload === "[DONE]") continue;
|
|
2711
|
+
let evt;
|
|
2712
|
+
try {
|
|
2713
|
+
evt = JSON.parse(payload);
|
|
2714
|
+
} catch {
|
|
2715
|
+
continue;
|
|
2716
|
+
}
|
|
2717
|
+
const choice = evt.choices?.[0];
|
|
2718
|
+
if (choice) {
|
|
2719
|
+
const delta = choice.delta;
|
|
2720
|
+
if (delta) {
|
|
2721
|
+
if (typeof delta.content === "string") text = (text ?? "") + delta.content;
|
|
2722
|
+
if (typeof delta.reasoning_opaque === "string") {
|
|
2723
|
+
reasoningOpaque = (reasoningOpaque ?? "") + delta.reasoning_opaque;
|
|
2724
|
+
}
|
|
2725
|
+
const deltaToolCalls = delta.tool_calls;
|
|
2726
|
+
for (const tc of deltaToolCalls ?? []) {
|
|
2727
|
+
const idx = typeof tc.index === "number" ? tc.index : 0;
|
|
2728
|
+
const fn = tc.function;
|
|
2729
|
+
const startsNewCall = typeof tc.id === "string" || typeof fn?.name === "string";
|
|
2730
|
+
let slot;
|
|
2731
|
+
if (startsNewCall) {
|
|
2732
|
+
slot = { args: "" };
|
|
2733
|
+
if (typeof tc.id === "string") slot.id = tc.id;
|
|
2734
|
+
if (fn?.name) slot.name = fn.name;
|
|
2735
|
+
toolSlots.push(slot);
|
|
2736
|
+
lastSlotByIndex.set(idx, slot);
|
|
2737
|
+
} else {
|
|
2738
|
+
slot = lastSlotByIndex.get(idx) ?? { args: "" };
|
|
2739
|
+
if (!lastSlotByIndex.has(idx)) {
|
|
2740
|
+
toolSlots.push(slot);
|
|
2741
|
+
lastSlotByIndex.set(idx, slot);
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
if (typeof fn?.arguments === "string") slot.args += fn.arguments;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
if (typeof choice.finish_reason === "string") finishReason = choice.finish_reason;
|
|
2748
|
+
if (typeof choice.reasoning_opaque === "string") {
|
|
2749
|
+
reasoningOpaque = choice.reasoning_opaque;
|
|
2750
|
+
}
|
|
2751
|
+
const msg = choice.message;
|
|
2752
|
+
if (typeof msg?.reasoning_opaque === "string") reasoningOpaque = msg.reasoning_opaque;
|
|
2753
|
+
}
|
|
2754
|
+
const usage2 = evt.usage;
|
|
2755
|
+
if (usage2) {
|
|
2756
|
+
promptTokens = usage2.prompt_tokens;
|
|
2757
|
+
completionTokens = usage2.completion_tokens;
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
}
|
|
2761
|
+
const toolCalls = toolSlots.filter((slot) => slot.id && slot.name).map((slot) => ({ id: slot.id, name: slot.name, input: safeJsonParse(slot.args) }));
|
|
2682
2762
|
return {
|
|
2683
|
-
text
|
|
2763
|
+
text,
|
|
2684
2764
|
toolCalls,
|
|
2685
|
-
stopReason: toolCalls.length > 0 ? "tool_use" :
|
|
2686
|
-
usage: this.usage(
|
|
2765
|
+
stopReason: toolCalls.length > 0 ? "tool_use" : finishReason === "length" ? "max_tokens" : "end",
|
|
2766
|
+
usage: this.usage(promptTokens, completionTokens),
|
|
2767
|
+
reasoningOpaque
|
|
2687
2768
|
};
|
|
2688
2769
|
}
|
|
2689
2770
|
buildResponsesInput(options) {
|
|
@@ -3951,13 +4032,13 @@ function selectToolsForRequest(params) {
|
|
|
3951
4032
|
addByName(name);
|
|
3952
4033
|
}
|
|
3953
4034
|
const relevantCategories = categoriesForMessage(userMessage);
|
|
3954
|
-
|
|
3955
|
-
if (selected.size >= maxTools) break;
|
|
4035
|
+
const relevantTools = allTools.map((tool, index) => ({ index, score: toolRelevanceScore(tool.name, userMessage), tool })).filter(({ tool }) => {
|
|
3956
4036
|
const cat = categoryForTool(tool.name);
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
4037
|
+
return cat !== "mcp" && relevantCategories.has(cat) && !selected.has(tool.name);
|
|
4038
|
+
}).sort((a, b) => b.score - a.score || a.index - b.index);
|
|
4039
|
+
for (const { tool } of relevantTools) {
|
|
4040
|
+
if (selected.size >= maxTools) break;
|
|
4041
|
+
selected.set(tool.name, tool);
|
|
3961
4042
|
}
|
|
3962
4043
|
if (selected.size < maxTools && relevantCategories.size === 0) {
|
|
3963
4044
|
for (const tool of allTools) {
|
|
@@ -3988,6 +4069,20 @@ function categoriesForMessage(message) {
|
|
|
3988
4069
|
}
|
|
3989
4070
|
return categories;
|
|
3990
4071
|
}
|
|
4072
|
+
function preloadRelevantCategoriesForRequest(loader, message) {
|
|
4073
|
+
if (typeof loader.loadCategory !== "function") return [];
|
|
4074
|
+
const loaded = [];
|
|
4075
|
+
for (const category of categoriesForMessage(message)) {
|
|
4076
|
+
if (category === "mcp" || category === "meta" || category === "skill") continue;
|
|
4077
|
+
const added = loader.loadCategory(category);
|
|
4078
|
+
if (added > 0) loaded.push(category);
|
|
4079
|
+
}
|
|
4080
|
+
return loaded;
|
|
4081
|
+
}
|
|
4082
|
+
function toolOutputLooksFailed(output) {
|
|
4083
|
+
if (!output) return false;
|
|
4084
|
+
return /^\s*\{?"?(error|success)"?\s*:\s*("|false)/i.test(output) || /\b(error|not found|enoent|failed)\b/i.test(output.slice(0, 300));
|
|
4085
|
+
}
|
|
3991
4086
|
function normalizeCategory(value) {
|
|
3992
4087
|
const normalized = value.toLowerCase();
|
|
3993
4088
|
if (["browser", "email", "teams", "office", "wechat", "memory", "files", "system"].includes(normalized)) {
|
|
@@ -4008,6 +4103,20 @@ function categoryForTool(toolName) {
|
|
|
4008
4103
|
if (toolName.startsWith("system_") || toolName.startsWith("clipboard_") || ["code_execute", "web_fetch", "web_search", "web_extract", "notify", "sleep", "ask_user", "config"].includes(toolName)) return "system";
|
|
4009
4104
|
return "mcp";
|
|
4010
4105
|
}
|
|
4106
|
+
function toolRelevanceScore(toolName, message) {
|
|
4107
|
+
const lower = message.toLowerCase();
|
|
4108
|
+
const name = toolName.toLowerCase();
|
|
4109
|
+
if (/https?:\/\//i.test(message) && ["web_extract", "web_fetch", "web_search"].includes(name)) return 150;
|
|
4110
|
+
if (/(?:^|[\s"'`])(?:~|\.\.?|[A-Za-z]:)?[\\/][^\s"'`]+|\b[\w.-]+\.(html?|md|txt|json|ya?ml|ts|tsx|js|jsx|py|csv|xlsx?|pptx?|pdf)\b/i.test(message) && ["file_read", "file_info", "file_list"].includes(name)) return 145;
|
|
4111
|
+
if (/\b(repo|repository|source code|github|codebase)\b/.test(lower) && ["system_run", "code_execute", "grep", "glob", "web_extract", "web_fetch", "file_read"].includes(name)) return 140;
|
|
4112
|
+
if (/https?:\/\//i.test(message) && ["browser_navigate", "browser_extract", "browser_snapshot"].includes(name)) return 135;
|
|
4113
|
+
if (/\b(powerpoint|pptx?|presentation|slides?|deck)\b/.test(lower) && ["powerpoint_focus", "powerpoint_new_presentation", "powerpoint_new_slide", "powerpoint_read_content", "powerpoint_send_keys", "powerpoint_screenshot"].includes(name)) return 130;
|
|
4114
|
+
if (/\b(powerpoint|pptx?|presentation|slides?|deck)\b/.test(lower) && name.startsWith("powerpoint_")) return 85;
|
|
4115
|
+
if (/\b(excel|spreadsheet|xlsx?|csv|data analysis)\b/.test(lower) && ["excel_focus", "excel_new_workbook", "excel_read_content", "excel_enter_value", "excel_enter_formula"].includes(name)) return 95;
|
|
4116
|
+
if (/\b(word|docx?|document|report)\b/.test(lower) && ["word_focus", "word_new_document", "word_read_content", "word_insert_text", "word_save"].includes(name)) return 95;
|
|
4117
|
+
if (name === "openjaw_load_tools" || name === "invoke_skill") return 90;
|
|
4118
|
+
return 0;
|
|
4119
|
+
}
|
|
4011
4120
|
var DEFAULT_OPENAI_MAX_TOOLS, MCP_AUTO_GROW_HARD_CAP, BUILTIN_HEADROOM, FOUNDATION_TOOL_NAMES, PROFILE_CATEGORIES, CATEGORY_KEYWORDS;
|
|
4012
4121
|
var init_tool_exposure = __esm({
|
|
4013
4122
|
"src/tool-exposure.ts"() {
|
|
@@ -4031,10 +4140,10 @@ var init_tool_exposure = __esm({
|
|
|
4031
4140
|
CATEGORY_KEYWORDS = [
|
|
4032
4141
|
{ category: "email", patterns: [/\b(email|mail|outlook|inbox|calendar|schedule|meeting|invite|today|tomorrow)\b/i] },
|
|
4033
4142
|
{ category: "teams", patterns: [/\b(teams|chat|channel|message|dm|meeting|standup|today|mention)\b/i] },
|
|
4034
|
-
{ category: "browser", patterns: [/\b(browser|page|website|web|navigate|click|screenshot|snapshot|console|image|search online)\b/i] },
|
|
4035
|
-
{ category: "files", patterns: [/\b(file|folder|directory|read|write|edit|grep|glob|find in repo|codebase)\b/i] },
|
|
4143
|
+
{ category: "browser", patterns: [/\b(browser|page|website|web|navigate|click|screenshot|snapshot|console|image|search online)\b/i, /https?:\/\//i] },
|
|
4144
|
+
{ category: "files", patterns: [/\b(file|folder|directory|read|write|edit|grep|glob|find in repo|codebase|source code|repo|repository|downloads?)\b/i, /(?:^|[\s"'`])(?:~|\.\.?|[A-Za-z]:)?[\\/][^\s"'`]+/i, /\b[\w.-]+\.(html?|md|txt|json|ya?ml|ts|tsx|js|jsx|py|csv|xlsx?|pptx?|pdf)\b/i] },
|
|
4036
4145
|
{ category: "system", patterns: [/\b(shell|command|terminal|run|execute|clipboard|notify|sleep|web search|fetch url|extract url|read url|article|docs?|paper|source page|news|latest|headlines|current events|breaking news)\b/i] },
|
|
4037
|
-
{ category: "office", patterns: [/\b(word|excel|powerpoint|spreadsheet|document|presentation|slide)\b/i] },
|
|
4146
|
+
{ category: "office", patterns: [/\b(word|excel|powerpoint|pptx?|spreadsheet|document|presentation|slide|deck)\b/i] },
|
|
4038
4147
|
{ category: "wechat", patterns: [/\b(wechat|weixin)\b/i] },
|
|
4039
4148
|
{ category: "memory", patterns: [/\b(memory|remember|recall|todo|preference)\b/i] }
|
|
4040
4149
|
];
|
|
@@ -4046,8 +4155,245 @@ var init_tool_exposure = __esm({
|
|
|
4046
4155
|
__name(rememberLoadedToolExposure, "rememberLoadedToolExposure");
|
|
4047
4156
|
__name(selectToolsForRequest, "selectToolsForRequest");
|
|
4048
4157
|
__name(categoriesForMessage, "categoriesForMessage");
|
|
4158
|
+
__name(preloadRelevantCategoriesForRequest, "preloadRelevantCategoriesForRequest");
|
|
4159
|
+
__name(toolOutputLooksFailed, "toolOutputLooksFailed");
|
|
4049
4160
|
__name(normalizeCategory, "normalizeCategory");
|
|
4050
4161
|
__name(categoryForTool, "categoryForTool");
|
|
4162
|
+
__name(toolRelevanceScore, "toolRelevanceScore");
|
|
4163
|
+
}
|
|
4164
|
+
});
|
|
4165
|
+
|
|
4166
|
+
// src/turn-control.ts
|
|
4167
|
+
var DEFAULT_MAX_TOOL_ROUNDS, IterationBudget;
|
|
4168
|
+
var init_turn_control = __esm({
|
|
4169
|
+
"src/turn-control.ts"() {
|
|
4170
|
+
"use strict";
|
|
4171
|
+
DEFAULT_MAX_TOOL_ROUNDS = 100;
|
|
4172
|
+
IterationBudget = class {
|
|
4173
|
+
static {
|
|
4174
|
+
__name(this, "IterationBudget");
|
|
4175
|
+
}
|
|
4176
|
+
used = 0;
|
|
4177
|
+
graceUsed = false;
|
|
4178
|
+
max;
|
|
4179
|
+
constructor(max = DEFAULT_MAX_TOOL_ROUNDS) {
|
|
4180
|
+
this.max = Math.max(1, Math.floor(max));
|
|
4181
|
+
}
|
|
4182
|
+
get remaining() {
|
|
4183
|
+
return Math.max(0, this.max - this.used);
|
|
4184
|
+
}
|
|
4185
|
+
get consumed() {
|
|
4186
|
+
return this.used;
|
|
4187
|
+
}
|
|
4188
|
+
/** True while there is budget (or the one-time grace round) left to run. */
|
|
4189
|
+
canContinue() {
|
|
4190
|
+
return this.remaining > 0 || !this.graceUsed;
|
|
4191
|
+
}
|
|
4192
|
+
/** True only when the budget is spent and we are on the grace round. */
|
|
4193
|
+
isGraceRound() {
|
|
4194
|
+
return this.remaining === 0 && !this.graceUsed;
|
|
4195
|
+
}
|
|
4196
|
+
/** Consume one round. Returns false if nothing (not even grace) is left. */
|
|
4197
|
+
consume() {
|
|
4198
|
+
if (this.remaining > 0) {
|
|
4199
|
+
this.used += 1;
|
|
4200
|
+
return true;
|
|
4201
|
+
}
|
|
4202
|
+
if (!this.graceUsed) {
|
|
4203
|
+
this.graceUsed = true;
|
|
4204
|
+
return true;
|
|
4205
|
+
}
|
|
4206
|
+
return false;
|
|
4207
|
+
}
|
|
4208
|
+
/** Give a round back (e.g. a round that made no model-visible progress). */
|
|
4209
|
+
refund() {
|
|
4210
|
+
if (this.used > 0) this.used -= 1;
|
|
4211
|
+
}
|
|
4212
|
+
};
|
|
4213
|
+
}
|
|
4214
|
+
});
|
|
4215
|
+
|
|
4216
|
+
// src/tool-guardrails.ts
|
|
4217
|
+
function isNoProgressTracked(toolName) {
|
|
4218
|
+
if (MUTATING_TOOLS.has(toolName)) return false;
|
|
4219
|
+
if (toolName.startsWith("powerpoint_") || toolName.startsWith("word_") || toolName.startsWith("excel_")) {
|
|
4220
|
+
return false;
|
|
4221
|
+
}
|
|
4222
|
+
return true;
|
|
4223
|
+
}
|
|
4224
|
+
function signatureOf(toolName, args) {
|
|
4225
|
+
return `${toolName}\0${canonicalJson(args ?? {})}`;
|
|
4226
|
+
}
|
|
4227
|
+
function canonicalJson(value) {
|
|
4228
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
|
|
4229
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
4230
|
+
const obj = value;
|
|
4231
|
+
const keys = Object.keys(obj).sort();
|
|
4232
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${canonicalJson(obj[k])}`).join(",")}}`;
|
|
4233
|
+
}
|
|
4234
|
+
function hashOutput(output) {
|
|
4235
|
+
let h = 2166136261;
|
|
4236
|
+
const s = output ?? "";
|
|
4237
|
+
for (let i = 0; i < s.length; i++) {
|
|
4238
|
+
h ^= s.charCodeAt(i);
|
|
4239
|
+
h = Math.imul(h, 16777619);
|
|
4240
|
+
}
|
|
4241
|
+
return (h >>> 0).toString(16);
|
|
4242
|
+
}
|
|
4243
|
+
function appendGuardrailGuidance(result, decision) {
|
|
4244
|
+
if (decision.action !== "warn" && decision.action !== "halt" || !decision.message) return result;
|
|
4245
|
+
const label = decision.action === "halt" ? "Tool loop hard stop" : "Tool loop warning";
|
|
4246
|
+
return `${result || ""}
|
|
4247
|
+
|
|
4248
|
+
[${label}: ${decision.code}; count=${decision.count}; ${decision.message}]`;
|
|
4249
|
+
}
|
|
4250
|
+
function blockedToolResult(decision) {
|
|
4251
|
+
return JSON.stringify({ error: decision.message, guardrail: { code: decision.code, action: decision.action } });
|
|
4252
|
+
}
|
|
4253
|
+
var DEFAULT_GUARDRAIL_THRESHOLDS, MUTATING_TOOLS, ToolLoopGuardrails;
|
|
4254
|
+
var init_tool_guardrails = __esm({
|
|
4255
|
+
"src/tool-guardrails.ts"() {
|
|
4256
|
+
"use strict";
|
|
4257
|
+
DEFAULT_GUARDRAIL_THRESHOLDS = {
|
|
4258
|
+
hardStopEnabled: true,
|
|
4259
|
+
exactFailureWarnAfter: 2,
|
|
4260
|
+
exactFailureBlockAfter: 5,
|
|
4261
|
+
sameToolFailureWarnAfter: 3,
|
|
4262
|
+
sameToolFailureHaltAfter: 8,
|
|
4263
|
+
noProgressWarnAfter: 2,
|
|
4264
|
+
noProgressBlockAfter: 5
|
|
4265
|
+
};
|
|
4266
|
+
MUTATING_TOOLS = /* @__PURE__ */ new Set([
|
|
4267
|
+
"file_write",
|
|
4268
|
+
"file_edit",
|
|
4269
|
+
"file_delete",
|
|
4270
|
+
"notify",
|
|
4271
|
+
"ask_user",
|
|
4272
|
+
"memory_append",
|
|
4273
|
+
"memory_save"
|
|
4274
|
+
]);
|
|
4275
|
+
__name(isNoProgressTracked, "isNoProgressTracked");
|
|
4276
|
+
__name(signatureOf, "signatureOf");
|
|
4277
|
+
__name(canonicalJson, "canonicalJson");
|
|
4278
|
+
__name(hashOutput, "hashOutput");
|
|
4279
|
+
ToolLoopGuardrails = class {
|
|
4280
|
+
static {
|
|
4281
|
+
__name(this, "ToolLoopGuardrails");
|
|
4282
|
+
}
|
|
4283
|
+
t;
|
|
4284
|
+
exactFailure = /* @__PURE__ */ new Map();
|
|
4285
|
+
sameToolFailure = /* @__PURE__ */ new Map();
|
|
4286
|
+
noProgress = /* @__PURE__ */ new Map();
|
|
4287
|
+
haltDecision = null;
|
|
4288
|
+
constructor(thresholds = {}) {
|
|
4289
|
+
this.t = { ...DEFAULT_GUARDRAIL_THRESHOLDS, ...thresholds };
|
|
4290
|
+
}
|
|
4291
|
+
/** Set once a block/halt has fired; the loop reads this to stop the turn. */
|
|
4292
|
+
get halted() {
|
|
4293
|
+
return this.haltDecision;
|
|
4294
|
+
}
|
|
4295
|
+
/**
|
|
4296
|
+
* Consult before (re-)executing a tool call. Returns `block` if this exact
|
|
4297
|
+
* call has already failed/no-progressed past the hard-stop threshold, so the
|
|
4298
|
+
* loop can skip execution and tell the model to change strategy.
|
|
4299
|
+
*/
|
|
4300
|
+
before(toolName, args) {
|
|
4301
|
+
if (!this.t.hardStopEnabled) return { action: "allow", toolName };
|
|
4302
|
+
const sig = signatureOf(toolName, args);
|
|
4303
|
+
const exact = this.exactFailure.get(sig) ?? 0;
|
|
4304
|
+
if (exact >= this.t.exactFailureBlockAfter) {
|
|
4305
|
+
return this.recordHalt({
|
|
4306
|
+
action: "block",
|
|
4307
|
+
code: "repeated_exact_failure_block",
|
|
4308
|
+
message: `Blocked ${toolName}: the same tool call failed ${exact} times with identical arguments. Stop retrying it unchanged; change strategy or explain the blocker.`,
|
|
4309
|
+
toolName,
|
|
4310
|
+
count: exact
|
|
4311
|
+
});
|
|
4312
|
+
}
|
|
4313
|
+
if (isNoProgressTracked(toolName)) {
|
|
4314
|
+
const rec = this.noProgress.get(sig);
|
|
4315
|
+
if (rec && rec.count >= this.t.noProgressBlockAfter) {
|
|
4316
|
+
return this.recordHalt({
|
|
4317
|
+
action: "block",
|
|
4318
|
+
code: "idempotent_no_progress_block",
|
|
4319
|
+
message: `Blocked ${toolName}: this call returned the same result ${rec.count} times. Stop repeating it unchanged; use the result already provided or try a different approach.`,
|
|
4320
|
+
toolName,
|
|
4321
|
+
count: rec.count
|
|
4322
|
+
});
|
|
4323
|
+
}
|
|
4324
|
+
}
|
|
4325
|
+
return { action: "allow", toolName };
|
|
4326
|
+
}
|
|
4327
|
+
/**
|
|
4328
|
+
* Record the outcome after a tool call. Returns a `warn` to append to the
|
|
4329
|
+
* tool result, or a `halt` to stop the turn. `failed` is derived by the
|
|
4330
|
+
* caller from the output shape (reuse tool-exposure's toolOutputLooksFailed).
|
|
4331
|
+
*/
|
|
4332
|
+
after(toolName, args, output, failed) {
|
|
4333
|
+
const sig = signatureOf(toolName, args);
|
|
4334
|
+
if (failed) {
|
|
4335
|
+
const exact = (this.exactFailure.get(sig) ?? 0) + 1;
|
|
4336
|
+
this.exactFailure.set(sig, exact);
|
|
4337
|
+
this.noProgress.delete(sig);
|
|
4338
|
+
const same = (this.sameToolFailure.get(toolName) ?? 0) + 1;
|
|
4339
|
+
this.sameToolFailure.set(toolName, same);
|
|
4340
|
+
if (this.t.hardStopEnabled && same >= this.t.sameToolFailureHaltAfter) {
|
|
4341
|
+
return this.recordHalt({
|
|
4342
|
+
action: "halt",
|
|
4343
|
+
code: "same_tool_failure_halt",
|
|
4344
|
+
message: `Stopped ${toolName}: it failed ${same} times this turn. Stop retrying the same failing tool path and choose a different approach.`,
|
|
4345
|
+
toolName,
|
|
4346
|
+
count: same
|
|
4347
|
+
});
|
|
4348
|
+
}
|
|
4349
|
+
if (exact >= this.t.exactFailureWarnAfter) {
|
|
4350
|
+
return {
|
|
4351
|
+
action: "warn",
|
|
4352
|
+
code: "repeated_exact_failure_warning",
|
|
4353
|
+
message: `${toolName} has failed ${exact} times with identical arguments. This looks like a loop; inspect the error and change strategy instead of retrying it unchanged.`,
|
|
4354
|
+
toolName,
|
|
4355
|
+
count: exact
|
|
4356
|
+
};
|
|
4357
|
+
}
|
|
4358
|
+
if (same >= this.t.sameToolFailureWarnAfter) {
|
|
4359
|
+
return {
|
|
4360
|
+
action: "warn",
|
|
4361
|
+
code: "same_tool_failure_warning",
|
|
4362
|
+
message: `${toolName} has failed ${same} times this turn. This looks like a loop. Do not switch to text-only replies; keep using tools, but inspect the latest error and verify your assumptions before retrying.`,
|
|
4363
|
+
toolName,
|
|
4364
|
+
count: same
|
|
4365
|
+
};
|
|
4366
|
+
}
|
|
4367
|
+
return { action: "allow", toolName, count: exact };
|
|
4368
|
+
}
|
|
4369
|
+
this.exactFailure.delete(sig);
|
|
4370
|
+
this.sameToolFailure.delete(toolName);
|
|
4371
|
+
if (!isNoProgressTracked(toolName)) {
|
|
4372
|
+
this.noProgress.delete(sig);
|
|
4373
|
+
return { action: "allow", toolName };
|
|
4374
|
+
}
|
|
4375
|
+
const hash3 = hashOutput(output);
|
|
4376
|
+
const prev = this.noProgress.get(sig);
|
|
4377
|
+
const count = prev && prev.hash === hash3 ? prev.count + 1 : 1;
|
|
4378
|
+
this.noProgress.set(sig, { hash: hash3, count });
|
|
4379
|
+
if (count >= this.t.noProgressWarnAfter) {
|
|
4380
|
+
return {
|
|
4381
|
+
action: "warn",
|
|
4382
|
+
code: "idempotent_no_progress_warning",
|
|
4383
|
+
message: `${toolName} returned the same result ${count} times. Use the result already provided or change the approach instead of repeating it unchanged.`,
|
|
4384
|
+
toolName,
|
|
4385
|
+
count
|
|
4386
|
+
};
|
|
4387
|
+
}
|
|
4388
|
+
return { action: "allow", toolName, count };
|
|
4389
|
+
}
|
|
4390
|
+
recordHalt(decision) {
|
|
4391
|
+
this.haltDecision = decision;
|
|
4392
|
+
return decision;
|
|
4393
|
+
}
|
|
4394
|
+
};
|
|
4395
|
+
__name(appendGuardrailGuidance, "appendGuardrailGuidance");
|
|
4396
|
+
__name(blockedToolResult, "blockedToolResult");
|
|
4051
4397
|
}
|
|
4052
4398
|
});
|
|
4053
4399
|
|
|
@@ -4600,6 +4946,8 @@ var init_agent_loop = __esm({
|
|
|
4600
4946
|
init_telemetry();
|
|
4601
4947
|
init_context_compressor();
|
|
4602
4948
|
init_tool_exposure();
|
|
4949
|
+
init_turn_control();
|
|
4950
|
+
init_tool_guardrails();
|
|
4603
4951
|
init_provider_auth();
|
|
4604
4952
|
init_connect();
|
|
4605
4953
|
AgentLoop = class {
|
|
@@ -5120,15 +5468,25 @@ ${summary}
|
|
|
5120
5468
|
const MAX_SAME_ACTIONS = 3;
|
|
5121
5469
|
let previousCacheReadTokens = 0;
|
|
5122
5470
|
let maxTokensContinuations = 0;
|
|
5471
|
+
const budget = new IterationBudget(this.config.llm.max_tool_rounds ?? DEFAULT_MAX_TOOL_ROUNDS);
|
|
5472
|
+
const guardrails = new ToolLoopGuardrails();
|
|
5123
5473
|
for (let step = 0; ; step++) {
|
|
5124
5474
|
if (signal.aborted) {
|
|
5125
5475
|
yield { type: "answer", content: "[Interrupted by user]" };
|
|
5126
5476
|
return;
|
|
5127
5477
|
}
|
|
5478
|
+
const forceFinalSummary = budget.isGraceRound();
|
|
5479
|
+
if (!budget.consume()) {
|
|
5480
|
+
const reason = "max_iterations";
|
|
5481
|
+
yield { type: "answer", content: `[Reached the maximum of ${budget.max} tool rounds. Stopping.]`, exitReason: reason };
|
|
5482
|
+
return;
|
|
5483
|
+
}
|
|
5128
5484
|
let responseText = null;
|
|
5129
5485
|
const responseToolCalls = [];
|
|
5130
5486
|
let responseStopReason = "end";
|
|
5487
|
+
let responseReasoningOpaque;
|
|
5131
5488
|
let responseUsage;
|
|
5489
|
+
preloadRelevantCategoriesForRequest(this.toolRegistry, userMessage);
|
|
5132
5490
|
const allTools = this.toolRegistry.listTools();
|
|
5133
5491
|
const exposure = selectToolsForRequest({
|
|
5134
5492
|
config: this.config,
|
|
@@ -5136,7 +5494,13 @@ ${summary}
|
|
|
5136
5494
|
userMessage,
|
|
5137
5495
|
state: this._toolExposureState
|
|
5138
5496
|
});
|
|
5139
|
-
const tools = exposure.tools;
|
|
5497
|
+
const tools = forceFinalSummary ? [] : exposure.tools;
|
|
5498
|
+
if (forceFinalSummary) {
|
|
5499
|
+
messages.push({
|
|
5500
|
+
role: "user",
|
|
5501
|
+
content: "[System: You've reached the maximum number of tool-calling rounds. Do not call any more tools. Summarize what you accomplished, what is still incomplete, and any blocker, as your final answer.]"
|
|
5502
|
+
});
|
|
5503
|
+
}
|
|
5140
5504
|
const chatOptions = {
|
|
5141
5505
|
systemPrompt,
|
|
5142
5506
|
messages,
|
|
@@ -5219,6 +5583,7 @@ ${summary}
|
|
|
5219
5583
|
responseToolCalls.push(...response.toolCalls);
|
|
5220
5584
|
responseStopReason = response.stopReason;
|
|
5221
5585
|
responseUsage = response.usage;
|
|
5586
|
+
responseReasoningOpaque = response.reasoningOpaque;
|
|
5222
5587
|
}
|
|
5223
5588
|
} catch (apiError) {
|
|
5224
5589
|
const errMsg = apiError instanceof Error ? apiError.message : String(apiError);
|
|
@@ -5345,10 +5710,11 @@ ${summary}
|
|
|
5345
5710
|
this.conversationHistory.push({ role: "assistant", content: responseText });
|
|
5346
5711
|
this.session.messages = this.conversationHistory;
|
|
5347
5712
|
saveSession(this.session);
|
|
5713
|
+
const exitReason = forceFinalSummary ? "max_iterations" : "completed";
|
|
5348
5714
|
if (!this.provider.chatStream) {
|
|
5349
|
-
yield { type: "answer", content: responseText ?? "" };
|
|
5715
|
+
yield { type: "answer", content: responseText ?? "", exitReason };
|
|
5350
5716
|
} else {
|
|
5351
|
-
yield { type: "answer", content: "" };
|
|
5717
|
+
yield { type: "answer", content: "", exitReason };
|
|
5352
5718
|
}
|
|
5353
5719
|
if (this._toolRoundsInRun >= 1 || responseText && responseText.length > 200) {
|
|
5354
5720
|
void this.postTurnMemorySave(systemPrompt, messages, tools, signal);
|
|
@@ -5380,7 +5746,8 @@ ${summary}
|
|
|
5380
5746
|
messages.push({
|
|
5381
5747
|
role: "assistant",
|
|
5382
5748
|
content: responseText,
|
|
5383
|
-
toolCalls: validToolCalls
|
|
5749
|
+
toolCalls: validToolCalls,
|
|
5750
|
+
reasoningOpaque: responseReasoningOpaque
|
|
5384
5751
|
});
|
|
5385
5752
|
const computerCalls = validToolCalls.filter((tc) => tc.name === "computer");
|
|
5386
5753
|
const otherCalls = validToolCalls.filter((tc) => tc.name !== "computer");
|
|
@@ -5388,6 +5755,10 @@ ${summary}
|
|
|
5388
5755
|
if (signal.aborted) {
|
|
5389
5756
|
return { id: tc.id, name: tc.name, output: "[Interrupted]", imageData: void 0 };
|
|
5390
5757
|
}
|
|
5758
|
+
const pre = guardrails.before(tc.name, tc.input);
|
|
5759
|
+
if (pre.action === "block") {
|
|
5760
|
+
return { id: tc.id, name: tc.name, output: blockedToolResult(pre), imageData: void 0 };
|
|
5761
|
+
}
|
|
5391
5762
|
let output;
|
|
5392
5763
|
let imageData2;
|
|
5393
5764
|
try {
|
|
@@ -5444,7 +5815,12 @@ ${summary}
|
|
|
5444
5815
|
}
|
|
5445
5816
|
} catch {
|
|
5446
5817
|
}
|
|
5447
|
-
|
|
5818
|
+
let outputStr = typeof output === "string" ? output : JSON.stringify(output ?? { error: "No output" });
|
|
5819
|
+
const failed = toolOutputLooksFailed(outputStr);
|
|
5820
|
+
const verdict = guardrails.after(tc.name, tc.input, outputStr, failed);
|
|
5821
|
+
if (verdict.action === "warn" || verdict.action === "halt") {
|
|
5822
|
+
outputStr = appendGuardrailGuidance(outputStr, verdict);
|
|
5823
|
+
}
|
|
5448
5824
|
return { id: tc.id, name: tc.name, output: outputStr, imageData: imageData2 };
|
|
5449
5825
|
}, "executeOne");
|
|
5450
5826
|
const results = [];
|
|
@@ -5472,18 +5848,18 @@ ${summary}
|
|
|
5472
5848
|
content: parsed.question,
|
|
5473
5849
|
choices: parsed.choices ?? void 0
|
|
5474
5850
|
};
|
|
5475
|
-
const userResponse = await new Promise((
|
|
5851
|
+
const userResponse = await new Promise((resolve6) => {
|
|
5476
5852
|
if (this._pendingAskUserResponse !== null) {
|
|
5477
5853
|
const buffered = this._pendingAskUserResponse;
|
|
5478
5854
|
this._pendingAskUserResponse = null;
|
|
5479
|
-
|
|
5855
|
+
resolve6(buffered);
|
|
5480
5856
|
return;
|
|
5481
5857
|
}
|
|
5482
|
-
this._askUserResolver =
|
|
5858
|
+
this._askUserResolver = resolve6;
|
|
5483
5859
|
setTimeout(() => {
|
|
5484
|
-
if (this._askUserResolver ===
|
|
5860
|
+
if (this._askUserResolver === resolve6) {
|
|
5485
5861
|
this._askUserResolver = null;
|
|
5486
|
-
|
|
5862
|
+
resolve6("[No response from user \u2014 timed out after 5 minutes]");
|
|
5487
5863
|
}
|
|
5488
5864
|
}, 5 * 60 * 1e3);
|
|
5489
5865
|
});
|
|
@@ -5504,6 +5880,15 @@ ${summary}
|
|
|
5504
5880
|
this.conversationHistory = [...messages];
|
|
5505
5881
|
this.session.messages = this.conversationHistory;
|
|
5506
5882
|
saveSession(this.session);
|
|
5883
|
+
if (guardrails.halted) {
|
|
5884
|
+
const reason = "guardrail_halt";
|
|
5885
|
+
yield {
|
|
5886
|
+
type: "answer",
|
|
5887
|
+
content: `[Stopped: ${guardrails.halted.message ?? "tool loop detected"}]`,
|
|
5888
|
+
exitReason: reason
|
|
5889
|
+
};
|
|
5890
|
+
return;
|
|
5891
|
+
}
|
|
5507
5892
|
if (this._toolRoundsInRun > 0 && this._toolRoundsInRun % 5 === 0) {
|
|
5508
5893
|
messages.push({
|
|
5509
5894
|
role: "user",
|
|
@@ -6146,7 +6531,7 @@ var init_browser = __esm({
|
|
|
6146
6531
|
await Page.domContentEventFired();
|
|
6147
6532
|
} else if (options.waitFor === "networkidle") {
|
|
6148
6533
|
await Page.loadEventFired();
|
|
6149
|
-
await new Promise((
|
|
6534
|
+
await new Promise((resolve6) => setTimeout(resolve6, 1e3));
|
|
6150
6535
|
}
|
|
6151
6536
|
const result = await Runtime.evaluate({
|
|
6152
6537
|
expression: "document.title"
|
|
@@ -6917,7 +7302,7 @@ var init_browser = __esm({
|
|
|
6917
7302
|
if (exists) {
|
|
6918
7303
|
return true;
|
|
6919
7304
|
}
|
|
6920
|
-
await new Promise((
|
|
7305
|
+
await new Promise((resolve6) => setTimeout(resolve6, 200));
|
|
6921
7306
|
}
|
|
6922
7307
|
return false;
|
|
6923
7308
|
}
|
|
@@ -7145,10 +7530,10 @@ function createBrowseTools(config, sharedBrowser) {
|
|
|
7145
7530
|
}
|
|
7146
7531
|
},
|
|
7147
7532
|
execute: /* @__PURE__ */ __name(async (input) => {
|
|
7148
|
-
const { join:
|
|
7533
|
+
const { join: join48 } = await import("node:path");
|
|
7149
7534
|
const { tmpdir: tmpdir13 } = await import("node:os");
|
|
7150
|
-
const { randomUUID:
|
|
7151
|
-
const screenshotPath =
|
|
7535
|
+
const { randomUUID: randomUUID15 } = await import("node:crypto");
|
|
7536
|
+
const screenshotPath = join48(tmpdir13(), `openjaw-browser-${randomUUID15().slice(0, 8)}.png`);
|
|
7152
7537
|
const screenshot = await browser.screenshot({ fullPage: false, path: screenshotPath });
|
|
7153
7538
|
const snapshot = await browser.snapshot({ full: false });
|
|
7154
7539
|
return {
|
|
@@ -7843,7 +8228,7 @@ var init_outlook_desktop = __esm({
|
|
|
7843
8228
|
}
|
|
7844
8229
|
}
|
|
7845
8230
|
sleep(ms) {
|
|
7846
|
-
return new Promise((
|
|
8231
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
7847
8232
|
}
|
|
7848
8233
|
};
|
|
7849
8234
|
}
|
|
@@ -8441,7 +8826,7 @@ var init_outlook_web = __esm({
|
|
|
8441
8826
|
await this.browser.typeChars(text);
|
|
8442
8827
|
}
|
|
8443
8828
|
sleep(ms) {
|
|
8444
|
-
return new Promise((
|
|
8829
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
8445
8830
|
}
|
|
8446
8831
|
};
|
|
8447
8832
|
}
|
|
@@ -8675,8 +9060,8 @@ var init_token_pool = __esm({
|
|
|
8675
9060
|
if (!existsSync8(legacyDir))
|
|
8676
9061
|
return;
|
|
8677
9062
|
try {
|
|
8678
|
-
const { readdirSync:
|
|
8679
|
-
const files =
|
|
9063
|
+
const { readdirSync: readdirSync8 } = __require("node:fs");
|
|
9064
|
+
const files = readdirSync8(legacyDir);
|
|
8680
9065
|
for (const file2 of files) {
|
|
8681
9066
|
if (!file2.endsWith(".json"))
|
|
8682
9067
|
continue;
|
|
@@ -8856,7 +9241,7 @@ var init_cdp_token_extractor = __esm({
|
|
|
8856
9241
|
}
|
|
8857
9242
|
logger_default.info("CDP: reloading tab for token refresh", { url: targetTab.url, audience });
|
|
8858
9243
|
await this.reloadTab(targetTab);
|
|
8859
|
-
await new Promise((
|
|
9244
|
+
await new Promise((resolve6) => setTimeout(resolve6, PAGE_RELOAD_WAIT_MS));
|
|
8860
9245
|
if (!targetTab.webSocketDebuggerUrl)
|
|
8861
9246
|
return [];
|
|
8862
9247
|
const freshPages = await this.listPages();
|
|
@@ -8973,19 +9358,19 @@ var init_cdp_token_extractor = __esm({
|
|
|
8973
9358
|
* Evaluate a JS expression in a tab and return the string result.
|
|
8974
9359
|
*/
|
|
8975
9360
|
async evaluateInTab(wsUrl, expression) {
|
|
8976
|
-
return new Promise((
|
|
9361
|
+
return new Promise((resolve6) => {
|
|
8977
9362
|
let ws;
|
|
8978
9363
|
try {
|
|
8979
9364
|
ws = new WebSocket(wsUrl);
|
|
8980
9365
|
} catch (err) {
|
|
8981
9366
|
logger_default.warn("CDP: WebSocket constructor failed", { wsUrl: wsUrl.substring(0, 60), error: String(err) });
|
|
8982
|
-
|
|
9367
|
+
resolve6(null);
|
|
8983
9368
|
return;
|
|
8984
9369
|
}
|
|
8985
9370
|
const timer = setTimeout(() => {
|
|
8986
9371
|
logger_default.warn("CDP: evaluateInTab timeout", { wsUrl: wsUrl.substring(0, 60) });
|
|
8987
9372
|
ws.close();
|
|
8988
|
-
|
|
9373
|
+
resolve6(null);
|
|
8989
9374
|
}, CDP_TIMEOUT_MS);
|
|
8990
9375
|
ws.on("open", () => {
|
|
8991
9376
|
ws.send(JSON.stringify({
|
|
@@ -8999,18 +9384,18 @@ var init_cdp_token_extractor = __esm({
|
|
|
8999
9384
|
if (resp.id === 1) {
|
|
9000
9385
|
clearTimeout(timer);
|
|
9001
9386
|
ws.close();
|
|
9002
|
-
|
|
9387
|
+
resolve6(resp.result?.result?.value ?? null);
|
|
9003
9388
|
}
|
|
9004
9389
|
});
|
|
9005
9390
|
ws.on("error", (err) => {
|
|
9006
9391
|
logger_default.warn("CDP: evaluateInTab WS error", { error: String(err), wsUrl: wsUrl.substring(0, 60) });
|
|
9007
9392
|
clearTimeout(timer);
|
|
9008
|
-
|
|
9393
|
+
resolve6(null);
|
|
9009
9394
|
});
|
|
9010
9395
|
});
|
|
9011
9396
|
}
|
|
9012
9397
|
async extractFromTab(wsUrl) {
|
|
9013
|
-
return new Promise((
|
|
9398
|
+
return new Promise((resolve6, reject) => {
|
|
9014
9399
|
const ws = new WebSocket(wsUrl);
|
|
9015
9400
|
const timer = setTimeout(() => {
|
|
9016
9401
|
ws.close();
|
|
@@ -9030,14 +9415,14 @@ var init_cdp_token_extractor = __esm({
|
|
|
9030
9415
|
ws.close();
|
|
9031
9416
|
const value = resp.result?.result?.value;
|
|
9032
9417
|
if (!value) {
|
|
9033
|
-
|
|
9418
|
+
resolve6([]);
|
|
9034
9419
|
return;
|
|
9035
9420
|
}
|
|
9036
9421
|
try {
|
|
9037
9422
|
const tokens = JSON.parse(value);
|
|
9038
|
-
|
|
9423
|
+
resolve6(tokens);
|
|
9039
9424
|
} catch {
|
|
9040
|
-
|
|
9425
|
+
resolve6([]);
|
|
9041
9426
|
}
|
|
9042
9427
|
}
|
|
9043
9428
|
});
|
|
@@ -9053,11 +9438,11 @@ var init_cdp_token_extractor = __esm({
|
|
|
9053
9438
|
async reloadTab(page) {
|
|
9054
9439
|
if (!page.webSocketDebuggerUrl)
|
|
9055
9440
|
return;
|
|
9056
|
-
return new Promise((
|
|
9441
|
+
return new Promise((resolve6, reject) => {
|
|
9057
9442
|
const ws = new WebSocket(page.webSocketDebuggerUrl);
|
|
9058
9443
|
const timer = setTimeout(() => {
|
|
9059
9444
|
ws.close();
|
|
9060
|
-
|
|
9445
|
+
resolve6();
|
|
9061
9446
|
}, 1e4);
|
|
9062
9447
|
ws.on("open", () => {
|
|
9063
9448
|
ws.send(JSON.stringify({
|
|
@@ -9071,7 +9456,7 @@ var init_cdp_token_extractor = __esm({
|
|
|
9071
9456
|
if (resp.id === 1) {
|
|
9072
9457
|
clearTimeout(timer);
|
|
9073
9458
|
ws.close();
|
|
9074
|
-
|
|
9459
|
+
resolve6();
|
|
9075
9460
|
}
|
|
9076
9461
|
});
|
|
9077
9462
|
ws.on("error", (err) => {
|
|
@@ -10732,13 +11117,13 @@ function createMemoryTools(config) {
|
|
|
10732
11117
|
const todos = input.todos;
|
|
10733
11118
|
try {
|
|
10734
11119
|
const { appendFile: appendFile2, mkdir: mkdir5 } = await import("node:fs/promises");
|
|
10735
|
-
const { existsSync:
|
|
10736
|
-
const { join:
|
|
10737
|
-
const { homedir:
|
|
10738
|
-
const memoryDir =
|
|
10739
|
-
if (!
|
|
11120
|
+
const { existsSync: existsSync34 } = await import("node:fs");
|
|
11121
|
+
const { join: join48 } = await import("node:path");
|
|
11122
|
+
const { homedir: homedir32 } = await import("node:os");
|
|
11123
|
+
const memoryDir = join48(homedir32(), ".openjaw", "memory");
|
|
11124
|
+
if (!existsSync34(memoryDir))
|
|
10740
11125
|
await mkdir5(memoryDir, { recursive: true });
|
|
10741
|
-
const todoPath =
|
|
11126
|
+
const todoPath = join48(memoryDir, "TODOS.md");
|
|
10742
11127
|
const { writeFile: writeFile5 } = await import("node:fs/promises");
|
|
10743
11128
|
await writeFile5(todoPath, `# Session Todos
|
|
10744
11129
|
|
|
@@ -12059,7 +12444,7 @@ var init_teams_desktop = __esm({
|
|
|
12059
12444
|
}
|
|
12060
12445
|
}
|
|
12061
12446
|
sleep(ms) {
|
|
12062
|
-
return new Promise((
|
|
12447
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
12063
12448
|
}
|
|
12064
12449
|
/**
|
|
12065
12450
|
* Get the current Teams window state
|
|
@@ -12926,7 +13311,7 @@ var init_teams_web = __esm({
|
|
|
12926
13311
|
}
|
|
12927
13312
|
}
|
|
12928
13313
|
sleep(ms) {
|
|
12929
|
-
return new Promise((
|
|
13314
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
12930
13315
|
}
|
|
12931
13316
|
};
|
|
12932
13317
|
}
|
|
@@ -14324,7 +14709,7 @@ var init_teams_chat_monitor = __esm({
|
|
|
14324
14709
|
return this.sentMessages.has(normalized);
|
|
14325
14710
|
}
|
|
14326
14711
|
sleep(ms) {
|
|
14327
|
-
return new Promise((
|
|
14712
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
14328
14713
|
}
|
|
14329
14714
|
/**
|
|
14330
14715
|
* Attempt to reconnect the browser after detecting a disconnection.
|
|
@@ -15111,7 +15496,7 @@ ${lines.join("\n")}`;
|
|
|
15111
15496
|
globalThis.__teamsSeenMessages.set(chatName, seenIds);
|
|
15112
15497
|
}
|
|
15113
15498
|
if (syncMode) {
|
|
15114
|
-
return new Promise((
|
|
15499
|
+
return new Promise((resolve6) => {
|
|
15115
15500
|
const timer2 = setInterval(async () => {
|
|
15116
15501
|
try {
|
|
15117
15502
|
const current = await channel.readCurrentChatMessages();
|
|
@@ -15120,7 +15505,7 @@ ${lines.join("\n")}`;
|
|
|
15120
15505
|
if (!seenIds.has(msgId) && msg.sender !== currentUserName) {
|
|
15121
15506
|
clearInterval(timer2);
|
|
15122
15507
|
seenIds.add(msgId);
|
|
15123
|
-
|
|
15508
|
+
resolve6({
|
|
15124
15509
|
success: true,
|
|
15125
15510
|
channel: channelType,
|
|
15126
15511
|
sync: true,
|
|
@@ -15197,7 +15582,7 @@ ${lines.join("\n")}`;
|
|
|
15197
15582
|
globalThis.__teamsSeenMessages.set(chatName, seenIds);
|
|
15198
15583
|
}
|
|
15199
15584
|
if (syncMode) {
|
|
15200
|
-
return new Promise((
|
|
15585
|
+
return new Promise((resolve6) => {
|
|
15201
15586
|
const timer2 = setInterval(async () => {
|
|
15202
15587
|
try {
|
|
15203
15588
|
const current = await channel.readCurrentChatMessages();
|
|
@@ -15206,7 +15591,7 @@ ${lines.join("\n")}`;
|
|
|
15206
15591
|
if (!seenIds.has(msgId) && msg.sender !== currentUserName) {
|
|
15207
15592
|
clearInterval(timer2);
|
|
15208
15593
|
seenIds.add(msgId);
|
|
15209
|
-
|
|
15594
|
+
resolve6({
|
|
15210
15595
|
success: true,
|
|
15211
15596
|
channel: channelType,
|
|
15212
15597
|
sync: true,
|
|
@@ -15564,7 +15949,7 @@ ${lines.join("\n")}`;
|
|
|
15564
15949
|
return allTools;
|
|
15565
15950
|
}
|
|
15566
15951
|
function sleep2(ms) {
|
|
15567
|
-
return new Promise((
|
|
15952
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
15568
15953
|
}
|
|
15569
15954
|
var MAX_STORED_ENTRIES, ENTRY_TTL_MS;
|
|
15570
15955
|
var init_chat = __esm({
|
|
@@ -16388,11 +16773,11 @@ function createShellTools(_config, hooks) {
|
|
|
16388
16773
|
const shell = input.shell ?? true;
|
|
16389
16774
|
if (input.background) {
|
|
16390
16775
|
const { tmpdir: tmpdir13 } = await import("node:os");
|
|
16391
|
-
const { join:
|
|
16392
|
-
const { randomUUID:
|
|
16776
|
+
const { join: join48 } = await import("node:path");
|
|
16777
|
+
const { randomUUID: randomUUID15 } = await import("node:crypto");
|
|
16393
16778
|
const { createWriteStream: createWriteStream2 } = await import("node:fs");
|
|
16394
|
-
const taskId =
|
|
16395
|
-
const outputPath =
|
|
16779
|
+
const taskId = randomUUID15().slice(0, 8);
|
|
16780
|
+
const outputPath = join48(tmpdir13(), `oj-bg-${taskId}.log`);
|
|
16396
16781
|
const detached = spawn(command, [], {
|
|
16397
16782
|
shell,
|
|
16398
16783
|
cwd,
|
|
@@ -16412,7 +16797,7 @@ function createShellTools(_config, hooks) {
|
|
|
16412
16797
|
message: `Command started in background (PID: ${detached.pid}). Output: ${outputPath}`
|
|
16413
16798
|
};
|
|
16414
16799
|
}
|
|
16415
|
-
return new Promise((
|
|
16800
|
+
return new Promise((resolve6) => {
|
|
16416
16801
|
const proc = spawn(command, [], {
|
|
16417
16802
|
shell,
|
|
16418
16803
|
cwd,
|
|
@@ -16430,7 +16815,7 @@ function createShellTools(_config, hooks) {
|
|
|
16430
16815
|
proc.on("close", (code) => {
|
|
16431
16816
|
const stdoutResult = truncateOutput(stdout.trim());
|
|
16432
16817
|
const stderrResult = truncateOutput(stderr.trim());
|
|
16433
|
-
|
|
16818
|
+
resolve6({
|
|
16434
16819
|
command,
|
|
16435
16820
|
exitCode: code,
|
|
16436
16821
|
stdout: stdoutResult.text,
|
|
@@ -16441,7 +16826,7 @@ function createShellTools(_config, hooks) {
|
|
|
16441
16826
|
});
|
|
16442
16827
|
});
|
|
16443
16828
|
proc.on("error", (error) => {
|
|
16444
|
-
|
|
16829
|
+
resolve6({
|
|
16445
16830
|
command,
|
|
16446
16831
|
exitCode: -1,
|
|
16447
16832
|
stdout: "",
|
|
@@ -16475,10 +16860,10 @@ function createShellTools(_config, hooks) {
|
|
|
16475
16860
|
},
|
|
16476
16861
|
requiresConfirmation: false,
|
|
16477
16862
|
execute: /* @__PURE__ */ __name(async (input) => {
|
|
16478
|
-
const { writeFileSync:
|
|
16479
|
-
const { join:
|
|
16863
|
+
const { writeFileSync: writeFileSync23, unlinkSync: unlinkSync9 } = await import("node:fs");
|
|
16864
|
+
const { join: join48 } = await import("node:path");
|
|
16480
16865
|
const { tmpdir: tmpdir13 } = await import("node:os");
|
|
16481
|
-
const { randomUUID:
|
|
16866
|
+
const { randomUUID: randomUUID15 } = await import("node:crypto");
|
|
16482
16867
|
const { execFile: execFile3 } = await import("node:child_process");
|
|
16483
16868
|
const code = input.code;
|
|
16484
16869
|
const language = input.language;
|
|
@@ -16495,14 +16880,14 @@ function createShellTools(_config, hooks) {
|
|
|
16495
16880
|
const interpreter = interpreterMap[language];
|
|
16496
16881
|
if (!interpreter)
|
|
16497
16882
|
return { error: `Unsupported language: ${language}` };
|
|
16498
|
-
const tmpFile =
|
|
16883
|
+
const tmpFile = join48(tmpdir13(), `oj-code-${randomUUID15().slice(0, 8)}${ext}`);
|
|
16499
16884
|
try {
|
|
16500
|
-
|
|
16885
|
+
writeFileSync23(tmpFile, code, "utf-8");
|
|
16501
16886
|
const startTime = Date.now();
|
|
16502
|
-
const result = await new Promise((
|
|
16887
|
+
const result = await new Promise((resolve6) => {
|
|
16503
16888
|
execFile3(interpreter.cmd, [...interpreter.args, tmpFile], { timeout, maxBuffer: 10 * 1024 * 1024 }, (error, stdout2, stderr2) => {
|
|
16504
16889
|
const exitCode = error ? typeof error.code === "number" ? error.code : 1 : 0;
|
|
16505
|
-
|
|
16890
|
+
resolve6({ exitCode, stdout: stdout2 ?? "", stderr: stderr2 ?? "" });
|
|
16506
16891
|
});
|
|
16507
16892
|
});
|
|
16508
16893
|
const executionTimeMs = Date.now() - startTime;
|
|
@@ -16595,7 +16980,7 @@ function createShellTools(_config, hooks) {
|
|
|
16595
16980
|
required: ["title", "message"]
|
|
16596
16981
|
},
|
|
16597
16982
|
execute: /* @__PURE__ */ __name(async (input) => {
|
|
16598
|
-
return new Promise((
|
|
16983
|
+
return new Promise((resolve6) => {
|
|
16599
16984
|
notifier.notify({
|
|
16600
16985
|
title: input.title,
|
|
16601
16986
|
message: input.message,
|
|
@@ -16603,9 +16988,9 @@ function createShellTools(_config, hooks) {
|
|
|
16603
16988
|
sound: true
|
|
16604
16989
|
}, (err) => {
|
|
16605
16990
|
if (err) {
|
|
16606
|
-
|
|
16991
|
+
resolve6({ error: err.message });
|
|
16607
16992
|
} else {
|
|
16608
|
-
|
|
16993
|
+
resolve6({ success: true });
|
|
16609
16994
|
}
|
|
16610
16995
|
});
|
|
16611
16996
|
});
|
|
@@ -16709,7 +17094,7 @@ function createShellTools(_config, hooks) {
|
|
|
16709
17094
|
},
|
|
16710
17095
|
execute: /* @__PURE__ */ __name(async (input) => {
|
|
16711
17096
|
const seconds = Math.min(Math.max(0.1, input.seconds), 60);
|
|
16712
|
-
await new Promise((
|
|
17097
|
+
await new Promise((resolve6) => setTimeout(resolve6, seconds * 1e3));
|
|
16713
17098
|
return { waited: seconds, message: `Waited ${seconds} seconds` };
|
|
16714
17099
|
}, "execute")
|
|
16715
17100
|
},
|
|
@@ -17821,7 +18206,7 @@ var init_office_desktop = __esm({
|
|
|
17821
18206
|
return Array.isArray(parsed) ? parsed : [parsed];
|
|
17822
18207
|
}
|
|
17823
18208
|
sleep(ms) {
|
|
17824
|
-
return new Promise((
|
|
18209
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
17825
18210
|
}
|
|
17826
18211
|
};
|
|
17827
18212
|
}
|
|
@@ -19502,7 +19887,7 @@ public class Win32Send {
|
|
|
19502
19887
|
}
|
|
19503
19888
|
}
|
|
19504
19889
|
sleep(ms) {
|
|
19505
|
-
return new Promise((
|
|
19890
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
19506
19891
|
}
|
|
19507
19892
|
};
|
|
19508
19893
|
}
|
|
@@ -19567,7 +19952,7 @@ function getActiveMonitors() {
|
|
|
19567
19952
|
return result;
|
|
19568
19953
|
}
|
|
19569
19954
|
function sleep3(ms) {
|
|
19570
|
-
return new Promise((
|
|
19955
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
19571
19956
|
}
|
|
19572
19957
|
async function fileHash(filePath) {
|
|
19573
19958
|
const data = fs.readFileSync(filePath);
|
|
@@ -21040,11 +21425,11 @@ function loadFlatSkillsFromDir(dir2, source, priority, out) {
|
|
|
21040
21425
|
function loadPackagedSkillsFromDir(dir2, out) {
|
|
21041
21426
|
for (const entry of safeReadDir(dir2)) {
|
|
21042
21427
|
if (!entry.isDirectory()) continue;
|
|
21043
|
-
const
|
|
21044
|
-
const entrypoint = findPackageEntrypoint(
|
|
21428
|
+
const rootDir2 = join22(dir2, entry.name);
|
|
21429
|
+
const entrypoint = findPackageEntrypoint(rootDir2);
|
|
21045
21430
|
if (!entrypoint) continue;
|
|
21046
|
-
const filePath = join22(
|
|
21047
|
-
const skill = parseSkillAtPath(filePath,
|
|
21431
|
+
const filePath = join22(rootDir2, entrypoint);
|
|
21432
|
+
const skill = parseSkillAtPath(filePath, rootDir2, entrypoint, "user", `${entry.name}.md`);
|
|
21048
21433
|
if (skill) putSkill(out, skill, 2);
|
|
21049
21434
|
}
|
|
21050
21435
|
}
|
|
@@ -21057,7 +21442,7 @@ function findPackageEntrypoint(dir2) {
|
|
|
21057
21442
|
const markdown = entries.filter((entry) => isMarkdown(entry.name));
|
|
21058
21443
|
return markdown.length === 1 ? markdown[0].name : null;
|
|
21059
21444
|
}
|
|
21060
|
-
function parseSkillAtPath(filePath,
|
|
21445
|
+
function parseSkillAtPath(filePath, rootDir2, entrypoint, source, fallbackFilename) {
|
|
21061
21446
|
try {
|
|
21062
21447
|
const content = readFileSync14(filePath, "utf-8").trim();
|
|
21063
21448
|
if (!content) return null;
|
|
@@ -21068,7 +21453,7 @@ function parseSkillAtPath(filePath, rootDir, entrypoint, source, fallbackFilenam
|
|
|
21068
21453
|
name,
|
|
21069
21454
|
meta: { ...parsed.meta, name },
|
|
21070
21455
|
filePath,
|
|
21071
|
-
rootDir,
|
|
21456
|
+
rootDir: rootDir2,
|
|
21072
21457
|
entrypoint,
|
|
21073
21458
|
source,
|
|
21074
21459
|
hasFrontmatter: parsed.hasFrontmatter
|
|
@@ -21725,12 +22110,12 @@ var init_telegram = __esm({
|
|
|
21725
22110
|
Options: ${chunk.choices.join(" | ")}` : "";
|
|
21726
22111
|
await this.bot.sendMessage(chatId, `\u2753 ${question}${choicesText}`);
|
|
21727
22112
|
updateStatus("\u2753 Waiting for your response...");
|
|
21728
|
-
const userReply = await new Promise((
|
|
21729
|
-
this._pendingReplyResolver =
|
|
22113
|
+
const userReply = await new Promise((resolve6) => {
|
|
22114
|
+
this._pendingReplyResolver = resolve6;
|
|
21730
22115
|
setTimeout(() => {
|
|
21731
|
-
if (this._pendingReplyResolver ===
|
|
22116
|
+
if (this._pendingReplyResolver === resolve6) {
|
|
21732
22117
|
this._pendingReplyResolver = null;
|
|
21733
|
-
|
|
22118
|
+
resolve6("[No response \u2014 timed out]");
|
|
21734
22119
|
}
|
|
21735
22120
|
}, 5 * 60 * 1e3);
|
|
21736
22121
|
});
|
|
@@ -22328,7 +22713,7 @@ async function promptConsent(servers) {
|
|
|
22328
22713
|
input: process.stdin,
|
|
22329
22714
|
output: process.stderr
|
|
22330
22715
|
});
|
|
22331
|
-
const ask = /* @__PURE__ */ __name((question) => new Promise((
|
|
22716
|
+
const ask = /* @__PURE__ */ __name((question) => new Promise((resolve6) => rl.question(question, resolve6)), "ask");
|
|
22332
22717
|
const w = process.stderr.columns || 80;
|
|
22333
22718
|
const inner = w - 4;
|
|
22334
22719
|
const hLine = "\u2500".repeat(inner);
|
|
@@ -24383,7 +24768,7 @@ try {
|
|
|
24383
24768
|
}
|
|
24384
24769
|
`;
|
|
24385
24770
|
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
24386
|
-
return new Promise((
|
|
24771
|
+
return new Promise((resolve6) => {
|
|
24387
24772
|
const proc = spawn3("powershell.exe", ["-NoProfile", "-STA", "-EncodedCommand", encoded], {
|
|
24388
24773
|
stdio: ["pipe", "pipe", "pipe"],
|
|
24389
24774
|
windowsHide: true
|
|
@@ -24398,22 +24783,22 @@ try {
|
|
|
24398
24783
|
});
|
|
24399
24784
|
const timer = setTimeout(() => {
|
|
24400
24785
|
if (!proc.killed) proc.kill();
|
|
24401
|
-
|
|
24786
|
+
resolve6(null);
|
|
24402
24787
|
}, (timeoutSeconds + 5) * 1e3);
|
|
24403
24788
|
proc.on("exit", () => {
|
|
24404
24789
|
clearTimeout(timer);
|
|
24405
24790
|
const output = stdout.replace(/#< CLIXML[\s\S]*/m, "").trim();
|
|
24406
24791
|
if (output === "NO_SPEECH" || output.startsWith("ERROR") || !output) {
|
|
24407
|
-
|
|
24792
|
+
resolve6(null);
|
|
24408
24793
|
return;
|
|
24409
24794
|
}
|
|
24410
24795
|
const parts = output.split("|");
|
|
24411
24796
|
const text = parts[0]?.trim();
|
|
24412
24797
|
const confidence = parseFloat(parts[1] || "0");
|
|
24413
24798
|
if (text) {
|
|
24414
|
-
|
|
24799
|
+
resolve6({ text, confidence: isNaN(confidence) ? 0.5 : confidence });
|
|
24415
24800
|
} else {
|
|
24416
|
-
|
|
24801
|
+
resolve6(null);
|
|
24417
24802
|
}
|
|
24418
24803
|
try {
|
|
24419
24804
|
if (existsSync21(resultFile)) unlinkSync5(resultFile);
|
|
@@ -24750,6 +25135,7 @@ var init_PromptInput = __esm({
|
|
|
24750
25135
|
{ name: "/repl", description: "\u{1F527} Start interactive code REPL" },
|
|
24751
25136
|
{ name: "/voice", description: "\u{1F50A} Toggle voice output (TTS)" },
|
|
24752
25137
|
{ name: "/fork", description: "\u{1F500} Spawn background sub-agent" },
|
|
25138
|
+
{ name: "/workflow", description: "\u{1F9ED} Run advisory dynamic workflow" },
|
|
24753
25139
|
{ name: "/tasks", description: "List background tasks" },
|
|
24754
25140
|
{ name: "/clear", description: "Clear conversation history" },
|
|
24755
25141
|
{ name: "/compact", description: "Summarize old messages to free context" },
|
|
@@ -24782,6 +25168,12 @@ var init_PromptInput = __esm({
|
|
|
24782
25168
|
{ name: "pause", description: "Pause a task: /schedule pause <id>" },
|
|
24783
25169
|
{ name: "resume", description: "Resume a task: /schedule resume <id>" }
|
|
24784
25170
|
],
|
|
25171
|
+
"/workflow": [
|
|
25172
|
+
{ name: "status", description: "Open live worker status" },
|
|
25173
|
+
{ name: "list", description: "List recent workflows" },
|
|
25174
|
+
{ name: "show <id>", description: "Show workflow summary" },
|
|
25175
|
+
{ name: "cancel <id>", description: "Cancel a run or worker" }
|
|
25176
|
+
],
|
|
24785
25177
|
"/repl": [
|
|
24786
25178
|
{ name: "python", description: "Python interactive shell" },
|
|
24787
25179
|
{ name: "node", description: "Node.js interactive shell" },
|
|
@@ -25985,8 +26377,8 @@ function isSensitivePath(resolved) {
|
|
|
25985
26377
|
if (re.test(normalized)) return true;
|
|
25986
26378
|
}
|
|
25987
26379
|
for (const re of SENSITIVE_FILE_PATTERNS) {
|
|
25988
|
-
const
|
|
25989
|
-
if (re.test(
|
|
26380
|
+
const basename5 = path2.basename(resolved);
|
|
26381
|
+
if (re.test(basename5)) return true;
|
|
25990
26382
|
}
|
|
25991
26383
|
return false;
|
|
25992
26384
|
}
|
|
@@ -26104,34 +26496,34 @@ ${output}
|
|
|
26104
26496
|
}
|
|
26105
26497
|
}
|
|
26106
26498
|
function expandUrl(ref, warnings) {
|
|
26107
|
-
return new Promise((
|
|
26499
|
+
return new Promise((resolve6) => {
|
|
26108
26500
|
const url = ref.target;
|
|
26109
26501
|
const mod = url.startsWith("https") ? https : http;
|
|
26110
26502
|
const req = mod.get(url, { timeout: 15e3 }, (res) => {
|
|
26111
26503
|
if (res.statusCode && (res.statusCode >= 300 && res.statusCode < 400) && res.headers.location) {
|
|
26112
26504
|
const redirectMod = res.headers.location.startsWith("https") ? https : http;
|
|
26113
26505
|
const req2 = redirectMod.get(res.headers.location, { timeout: 15e3 }, (res2) => {
|
|
26114
|
-
collectResponse(res2, ref, warnings,
|
|
26506
|
+
collectResponse(res2, ref, warnings, resolve6);
|
|
26115
26507
|
});
|
|
26116
26508
|
req2.on("error", (e) => {
|
|
26117
26509
|
warnings.push(`\u26A0\uFE0F @url fetch error: ${e.message}`);
|
|
26118
|
-
|
|
26510
|
+
resolve6(null);
|
|
26119
26511
|
});
|
|
26120
26512
|
return;
|
|
26121
26513
|
}
|
|
26122
|
-
collectResponse(res, ref, warnings,
|
|
26514
|
+
collectResponse(res, ref, warnings, resolve6);
|
|
26123
26515
|
});
|
|
26124
26516
|
req.on("error", (e) => {
|
|
26125
26517
|
warnings.push(`\u26A0\uFE0F @url fetch error: ${e.message}`);
|
|
26126
|
-
|
|
26518
|
+
resolve6(null);
|
|
26127
26519
|
});
|
|
26128
26520
|
});
|
|
26129
26521
|
}
|
|
26130
|
-
function collectResponse(res, ref, warnings,
|
|
26522
|
+
function collectResponse(res, ref, warnings, resolve6) {
|
|
26131
26523
|
if (res.statusCode && res.statusCode >= 400) {
|
|
26132
26524
|
warnings.push(`\u26A0\uFE0F @url returned HTTP ${res.statusCode}: ${ref.target}`);
|
|
26133
26525
|
res.resume();
|
|
26134
|
-
|
|
26526
|
+
resolve6(null);
|
|
26135
26527
|
return;
|
|
26136
26528
|
}
|
|
26137
26529
|
const chunks = [];
|
|
@@ -26143,14 +26535,14 @@ function collectResponse(res, ref, warnings, resolve5) {
|
|
|
26143
26535
|
text = text.slice(0, 5e4) + "\n\u2026 (truncated)";
|
|
26144
26536
|
}
|
|
26145
26537
|
const tokens = estimateTokens2(text);
|
|
26146
|
-
|
|
26538
|
+
resolve6(`\u{1F310} @url:${ref.target} (${tokens} tokens)
|
|
26147
26539
|
\`\`\`
|
|
26148
26540
|
${text}
|
|
26149
26541
|
\`\`\``);
|
|
26150
26542
|
});
|
|
26151
26543
|
res.on("error", (e) => {
|
|
26152
26544
|
warnings.push(`\u26A0\uFE0F @url fetch error: ${e.message}`);
|
|
26153
|
-
|
|
26545
|
+
resolve6(null);
|
|
26154
26546
|
});
|
|
26155
26547
|
}
|
|
26156
26548
|
function stripHtml(html) {
|
|
@@ -26651,13 +27043,13 @@ Type /resume <id> to resume.` });
|
|
|
26651
27043
|
if (input === "/export") {
|
|
26652
27044
|
try {
|
|
26653
27045
|
const { writeFile: writeFile5, mkdir: mkdir5 } = await import("node:fs/promises");
|
|
26654
|
-
const { join:
|
|
26655
|
-
const { homedir:
|
|
26656
|
-
const { existsSync:
|
|
26657
|
-
const exportDir =
|
|
26658
|
-
if (!
|
|
27046
|
+
const { join: join48 } = await import("node:path");
|
|
27047
|
+
const { homedir: homedir32 } = await import("node:os");
|
|
27048
|
+
const { existsSync: existsSync34 } = await import("node:fs");
|
|
27049
|
+
const exportDir = join48(homedir32(), ".openjaw-agent", "exports");
|
|
27050
|
+
if (!existsSync34(exportDir)) await mkdir5(exportDir, { recursive: true });
|
|
26659
27051
|
const filename = `session-${agentLoop.sessionId}.md`;
|
|
26660
|
-
const filepath =
|
|
27052
|
+
const filepath = join48(exportDir, filename);
|
|
26661
27053
|
const lines = [
|
|
26662
27054
|
`# OpenJaw Agent Session ${agentLoop.sessionId}`,
|
|
26663
27055
|
`Date: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
@@ -26876,9 +27268,9 @@ ${list}` });
|
|
|
26876
27268
|
const cmd = lang === "python" ? "python" : lang === "node" ? "node" : "pwsh";
|
|
26877
27269
|
const args = lang === "python" ? ["-i", "-u"] : lang === "node" ? ["-i"] : ["-NoProfile", "-NoLogo"];
|
|
26878
27270
|
try {
|
|
26879
|
-
const { spawn:
|
|
27271
|
+
const { spawn: spawn8 } = await import("node:child_process");
|
|
26880
27272
|
if (replRef.current) replRef.current.kill();
|
|
26881
|
-
const proc =
|
|
27273
|
+
const proc = spawn8(cmd, args, {
|
|
26882
27274
|
stdio: ["pipe", "pipe", "pipe"],
|
|
26883
27275
|
windowsHide: true
|
|
26884
27276
|
});
|
|
@@ -26918,7 +27310,7 @@ ${list}` });
|
|
|
26918
27310
|
if (replRef.current && replLangRef.current && !input.startsWith("/")) {
|
|
26919
27311
|
const proc = replRef.current;
|
|
26920
27312
|
proc.stdin?.write(input + "\n");
|
|
26921
|
-
await new Promise((
|
|
27313
|
+
await new Promise((resolve6) => setTimeout(resolve6, 800));
|
|
26922
27314
|
const flush = proc._ojFlush;
|
|
26923
27315
|
const output = flush ? flush() : "";
|
|
26924
27316
|
if (output.trim()) {
|
|
@@ -27232,14 +27624,14 @@ Options: ${chunk.choices.join(" | ")}` : chunk.content;
|
|
|
27232
27624
|
waitingForAskUserRef.current = true;
|
|
27233
27625
|
setIsRunning(false);
|
|
27234
27626
|
setWaitingForAskUser(true);
|
|
27235
|
-
await new Promise((
|
|
27627
|
+
await new Promise((resolve6) => {
|
|
27236
27628
|
const checkInterval = setInterval(() => {
|
|
27237
27629
|
if (!waitingForAskUserRef.current || !agentLoop.isWaitingForAskUser) {
|
|
27238
27630
|
clearInterval(checkInterval);
|
|
27239
27631
|
waitingForAskUserRef.current = false;
|
|
27240
27632
|
setWaitingForAskUser(false);
|
|
27241
27633
|
setIsRunning(true);
|
|
27242
|
-
|
|
27634
|
+
resolve6();
|
|
27243
27635
|
}
|
|
27244
27636
|
}, 200);
|
|
27245
27637
|
});
|
|
@@ -27535,8 +27927,8 @@ async function withTimeout(promise, ms) {
|
|
|
27535
27927
|
try {
|
|
27536
27928
|
return await Promise.race([
|
|
27537
27929
|
promise,
|
|
27538
|
-
new Promise((
|
|
27539
|
-
timeoutId = setTimeout(() =>
|
|
27930
|
+
new Promise((resolve6) => {
|
|
27931
|
+
timeoutId = setTimeout(() => resolve6(SKILL_TIMEOUT), ms);
|
|
27540
27932
|
})
|
|
27541
27933
|
]);
|
|
27542
27934
|
} finally {
|
|
@@ -28146,12 +28538,12 @@ var init_teams = __esm({
|
|
|
28146
28538
|
Options: ${chunk.choices.join(" | ")}` : "";
|
|
28147
28539
|
await this.sendToSelfChat(`\u2753 ${question}${choicesText}`);
|
|
28148
28540
|
if (!answerSent) await updateStatus("\u{1F916} OpenJaw Agent \u2014 \u2753 Waiting for your response...");
|
|
28149
|
-
const userReply = await new Promise((
|
|
28150
|
-
this._pendingReplyResolver =
|
|
28541
|
+
const userReply = await new Promise((resolve6) => {
|
|
28542
|
+
this._pendingReplyResolver = resolve6;
|
|
28151
28543
|
setTimeout(() => {
|
|
28152
|
-
if (this._pendingReplyResolver ===
|
|
28544
|
+
if (this._pendingReplyResolver === resolve6) {
|
|
28153
28545
|
this._pendingReplyResolver = null;
|
|
28154
|
-
|
|
28546
|
+
resolve6("[No response \u2014 timed out]");
|
|
28155
28547
|
}
|
|
28156
28548
|
}, 5 * 60 * 1e3);
|
|
28157
28549
|
});
|
|
@@ -28635,12 +29027,12 @@ ${err.stack?.split("\n").slice(0, 3).join("\n")}` : String(err);
|
|
|
28635
29027
|
}
|
|
28636
29028
|
/** Wait for the next user message (used by ask_user flow) */
|
|
28637
29029
|
waitForUserReply(_chatId) {
|
|
28638
|
-
return new Promise((
|
|
28639
|
-
this._pendingReplyResolver =
|
|
29030
|
+
return new Promise((resolve6) => {
|
|
29031
|
+
this._pendingReplyResolver = resolve6;
|
|
28640
29032
|
setTimeout(() => {
|
|
28641
|
-
if (this._pendingReplyResolver ===
|
|
29033
|
+
if (this._pendingReplyResolver === resolve6) {
|
|
28642
29034
|
this._pendingReplyResolver = null;
|
|
28643
|
-
|
|
29035
|
+
resolve6("[No response \u2014 timed out]");
|
|
28644
29036
|
}
|
|
28645
29037
|
}, 5 * 60 * 1e3);
|
|
28646
29038
|
});
|
|
@@ -28931,9 +29323,9 @@ var init_wechat2 = __esm({
|
|
|
28931
29323
|
try {
|
|
28932
29324
|
const qrTerminal = await import("qrcode-terminal");
|
|
28933
29325
|
const mod = qrTerminal.default || qrTerminal;
|
|
28934
|
-
const qrAscii = await new Promise((
|
|
29326
|
+
const qrAscii = await new Promise((resolve6, reject) => {
|
|
28935
29327
|
try {
|
|
28936
|
-
mod.generate(qrUrl, { small: true }, (out) =>
|
|
29328
|
+
mod.generate(qrUrl, { small: true }, (out) => resolve6(out));
|
|
28937
29329
|
} catch (e) {
|
|
28938
29330
|
reject(e);
|
|
28939
29331
|
}
|
|
@@ -29199,12 +29591,12 @@ Scan URL manually: ${qrUrl}` });
|
|
|
29199
29591
|
const choicesText = chunk.choices?.length ? `
|
|
29200
29592
|
\u9009\u9879: ${chunk.choices.join(" | ")}` : "";
|
|
29201
29593
|
await this.sendText(userId, `\u2753 ${question}${choicesText}`, contextToken);
|
|
29202
|
-
const userReply = await new Promise((
|
|
29203
|
-
this._pendingReplyResolver =
|
|
29594
|
+
const userReply = await new Promise((resolve6) => {
|
|
29595
|
+
this._pendingReplyResolver = resolve6;
|
|
29204
29596
|
setTimeout(() => {
|
|
29205
|
-
if (this._pendingReplyResolver ===
|
|
29597
|
+
if (this._pendingReplyResolver === resolve6) {
|
|
29206
29598
|
this._pendingReplyResolver = null;
|
|
29207
|
-
|
|
29599
|
+
resolve6("[No response \u2014 timed out]");
|
|
29208
29600
|
}
|
|
29209
29601
|
}, 5 * 60 * 1e3);
|
|
29210
29602
|
});
|
|
@@ -30705,8 +31097,8 @@ function createPromptCollector(bus, getSessionId) {
|
|
|
30705
31097
|
}, "emit");
|
|
30706
31098
|
const register = /* @__PURE__ */ __name((kind) => {
|
|
30707
31099
|
const requestId = randomUUID12();
|
|
30708
|
-
const promise = new Promise((
|
|
30709
|
-
pending.set(requestId, { kind, resolve:
|
|
31100
|
+
const promise = new Promise((resolve6) => {
|
|
31101
|
+
pending.set(requestId, { kind, resolve: resolve6 });
|
|
30710
31102
|
});
|
|
30711
31103
|
return { promise, requestId };
|
|
30712
31104
|
}, "register");
|
|
@@ -31686,11 +32078,11 @@ function detectTerminal() {
|
|
|
31686
32078
|
}
|
|
31687
32079
|
return process.env.TERM ?? null;
|
|
31688
32080
|
}
|
|
31689
|
-
function supportsOsc52Clipboard(
|
|
31690
|
-
return OSC52_CAPABLE_TERMINALS.includes(
|
|
32081
|
+
function supportsOsc52Clipboard(terminal2 = env.terminal) {
|
|
32082
|
+
return OSC52_CAPABLE_TERMINALS.includes(terminal2 ?? "");
|
|
31691
32083
|
}
|
|
31692
32084
|
function execFileNoThrow(file2, args, options = {}) {
|
|
31693
|
-
return new Promise((
|
|
32085
|
+
return new Promise((resolve6) => {
|
|
31694
32086
|
const child = spawn4(file2, args, {
|
|
31695
32087
|
cwd: options.useCwd ? process.cwd() : void 0,
|
|
31696
32088
|
env: options.env,
|
|
@@ -31713,13 +32105,13 @@ function execFileNoThrow(file2, args, options = {}) {
|
|
|
31713
32105
|
if (timer) {
|
|
31714
32106
|
clearTimeout(timer);
|
|
31715
32107
|
}
|
|
31716
|
-
|
|
32108
|
+
resolve6({ stdout, stderr, code: 1, error: String(error) });
|
|
31717
32109
|
});
|
|
31718
32110
|
child.on("close", (code) => {
|
|
31719
32111
|
if (timer) {
|
|
31720
32112
|
clearTimeout(timer);
|
|
31721
32113
|
}
|
|
31722
|
-
|
|
32114
|
+
resolve6({ stdout, stderr, code: timedOut ? 124 : code ?? 0 });
|
|
31723
32115
|
});
|
|
31724
32116
|
if (options.input) {
|
|
31725
32117
|
child.stdin?.write(options.input);
|
|
@@ -31751,7 +32143,7 @@ function shouldEmitClipboardSequence(env2 = process.env) {
|
|
|
31751
32143
|
}
|
|
31752
32144
|
return !!env2["SSH_CONNECTION"] || !env2["TMUX"] && !env2["STY"];
|
|
31753
32145
|
}
|
|
31754
|
-
function shouldUseNativeClipboard(env2 = process.env,
|
|
32146
|
+
function shouldUseNativeClipboard(env2 = process.env, terminal2 = env.terminal) {
|
|
31755
32147
|
if (env2.SSH_CONNECTION) {
|
|
31756
32148
|
return false;
|
|
31757
32149
|
}
|
|
@@ -31761,7 +32153,7 @@ function shouldUseNativeClipboard(env2 = process.env, terminal = env.terminal) {
|
|
|
31761
32153
|
if (!shouldEmitClipboardSequence(env2)) {
|
|
31762
32154
|
return true;
|
|
31763
32155
|
}
|
|
31764
|
-
return !supportsOsc52Clipboard(
|
|
32156
|
+
return !supportsOsc52Clipboard(terminal2);
|
|
31765
32157
|
}
|
|
31766
32158
|
function tmuxPassthrough(payload) {
|
|
31767
32159
|
return `${ESC}Ptmux;${payload.replaceAll(ESC, ESC + ESC)}${ST}`;
|
|
@@ -33197,7 +33589,7 @@ function needsAltScreenResizeScrollbackClear(env2 = process.env) {
|
|
|
33197
33589
|
function supportsExtendedKeys() {
|
|
33198
33590
|
return EXTENDED_KEYS_TERMINALS.includes(env.terminal ?? "");
|
|
33199
33591
|
}
|
|
33200
|
-
function writeDiffToTerminal(
|
|
33592
|
+
function writeDiffToTerminal(terminal2, diff2, skipSyncMarkers = false, onDrain) {
|
|
33201
33593
|
if (diff2.length === 0) {
|
|
33202
33594
|
return { bytes: 0, backpressure: false };
|
|
33203
33595
|
}
|
|
@@ -33242,7 +33634,7 @@ function writeDiffToTerminal(terminal, diff2, skipSyncMarkers = false, onDrain)
|
|
|
33242
33634
|
if (useSync) {
|
|
33243
33635
|
buffer += ESU;
|
|
33244
33636
|
}
|
|
33245
|
-
const wrote = onDrain ?
|
|
33637
|
+
const wrote = onDrain ? terminal2.stdout.write(buffer, () => onDrain()) : terminal2.stdout.write(buffer);
|
|
33246
33638
|
return { bytes: Buffer.byteLength(buffer, "utf8"), backpressure: !wrote };
|
|
33247
33639
|
}
|
|
33248
33640
|
function logForDebugging(_message, _options = {}) {
|
|
@@ -36692,8 +37084,8 @@ function setTerminalFocused(v) {
|
|
|
36692
37084
|
cb();
|
|
36693
37085
|
}
|
|
36694
37086
|
if (!v) {
|
|
36695
|
-
for (const
|
|
36696
|
-
|
|
37087
|
+
for (const resolve6 of resolvers) {
|
|
37088
|
+
resolve6();
|
|
36697
37089
|
}
|
|
36698
37090
|
resolvers.clear();
|
|
36699
37091
|
}
|
|
@@ -42931,11 +43323,11 @@ $ npm install --save-dev react-devtools-core
|
|
|
42931
43323
|
* and the terminal doesn't respond, the promise remains pending.
|
|
42932
43324
|
*/
|
|
42933
43325
|
send(query) {
|
|
42934
|
-
return new Promise((
|
|
43326
|
+
return new Promise((resolve6) => {
|
|
42935
43327
|
this.queue.push({
|
|
42936
43328
|
kind: "query",
|
|
42937
43329
|
match: query.match,
|
|
42938
|
-
resolve: /* @__PURE__ */ __name((r) =>
|
|
43330
|
+
resolve: /* @__PURE__ */ __name((r) => resolve6(r), "resolve")
|
|
42939
43331
|
});
|
|
42940
43332
|
this.stdout.write(query.request);
|
|
42941
43333
|
});
|
|
@@ -42950,8 +43342,8 @@ $ npm install --save-dev react-devtools-core
|
|
|
42950
43342
|
* Safe to call with no pending queries — still waits for a round-trip.
|
|
42951
43343
|
*/
|
|
42952
43344
|
flush() {
|
|
42953
|
-
return new Promise((
|
|
42954
|
-
this.queue.push({ kind: "sentinel", resolve:
|
|
43345
|
+
return new Promise((resolve6) => {
|
|
43346
|
+
this.queue.push({ kind: "sentinel", resolve: resolve6 });
|
|
42955
43347
|
this.stdout.write(SENTINEL);
|
|
42956
43348
|
});
|
|
42957
43349
|
}
|
|
@@ -45431,8 +45823,8 @@ $ npm install --save-dev react-devtools-core
|
|
|
45431
45823
|
}
|
|
45432
45824
|
}
|
|
45433
45825
|
async waitUntilExit() {
|
|
45434
|
-
this.exitPromise ||= new Promise((
|
|
45435
|
-
this.resolveExitPromise =
|
|
45826
|
+
this.exitPromise ||= new Promise((resolve6, reject) => {
|
|
45827
|
+
this.resolveExitPromise = resolve6;
|
|
45436
45828
|
this.rejectExitPromise = reject;
|
|
45437
45829
|
});
|
|
45438
45830
|
return this.exitPromise;
|
|
@@ -45923,10 +46315,10 @@ async function writeClipboardText(text, platform2 = process.platform, start = sp
|
|
|
45923
46315
|
const candidates = writeClipboardCommands(platform2, env2);
|
|
45924
46316
|
for (const { cmd, args } of candidates) {
|
|
45925
46317
|
try {
|
|
45926
|
-
const ok = await new Promise((
|
|
46318
|
+
const ok = await new Promise((resolve6) => {
|
|
45927
46319
|
const child = start(cmd, [...args], { stdio: ["pipe", "ignore", "ignore"], windowsHide: true });
|
|
45928
|
-
child.once("error", () =>
|
|
45929
|
-
child.once("close", (code) =>
|
|
46320
|
+
child.once("error", () => resolve6(false));
|
|
46321
|
+
child.once("close", (code) => resolve6(code === 0));
|
|
45930
46322
|
child.stdin?.end(text);
|
|
45931
46323
|
});
|
|
45932
46324
|
if (ok) {
|
|
@@ -45985,8 +46377,8 @@ async function readOsc52Clipboard(querier, timeoutMs = 500) {
|
|
|
45985
46377
|
if (!querier) {
|
|
45986
46378
|
return null;
|
|
45987
46379
|
}
|
|
45988
|
-
const timeout = new Promise((
|
|
45989
|
-
setTimeout(() =>
|
|
46380
|
+
const timeout = new Promise((resolve6) => {
|
|
46381
|
+
setTimeout(() => resolve6(void 0), timeoutMs);
|
|
45990
46382
|
});
|
|
45991
46383
|
const query = querier.send({
|
|
45992
46384
|
request: buildOsc52ClipboardQuery(),
|
|
@@ -46143,12 +46535,12 @@ async function backupFile(filePath, ops) {
|
|
|
46143
46535
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
46144
46536
|
await ops.copyFile(filePath, `${filePath}.backup.${stamp}`);
|
|
46145
46537
|
}
|
|
46146
|
-
async function configureTerminalKeybindings(
|
|
46538
|
+
async function configureTerminalKeybindings(terminal2, options) {
|
|
46147
46539
|
const env2 = options?.env ?? process.env;
|
|
46148
46540
|
const platform2 = options?.platform ?? process.platform;
|
|
46149
46541
|
const homeDir = options?.homeDir ?? homedir25();
|
|
46150
46542
|
const ops = { ...DEFAULT_FILE_OPS, ...options?.fileOps ?? {} };
|
|
46151
|
-
const meta = TERMINAL_META[
|
|
46543
|
+
const meta = TERMINAL_META[terminal2];
|
|
46152
46544
|
if (isRemoteShellSession(env2)) {
|
|
46153
46545
|
return {
|
|
46154
46546
|
success: false,
|
|
@@ -46343,6 +46735,7 @@ var init_overlayStore = __esm({
|
|
|
46343
46735
|
buildOverlayState = /* @__PURE__ */ __name(() => ({
|
|
46344
46736
|
agents: false,
|
|
46345
46737
|
agentsInitialHistoryIndex: 0,
|
|
46738
|
+
agentsWorkflowId: null,
|
|
46346
46739
|
approval: null,
|
|
46347
46740
|
clarify: null,
|
|
46348
46741
|
confirm: null,
|
|
@@ -46358,13 +46751,14 @@ var init_overlayStore = __esm({
|
|
|
46358
46751
|
$overlayState = atom2(buildOverlayState());
|
|
46359
46752
|
$isBlocked = computed2(
|
|
46360
46753
|
$overlayState,
|
|
46361
|
-
({ agents, approval, clarify, confirm, mcpHub,
|
|
46754
|
+
({ agents, approval, clarify, confirm, mcpHub, pager, picker, secret, skillsHub, sudo }) => Boolean(agents || approval || clarify || confirm || mcpHub || pager || picker || secret || skillsHub || sudo)
|
|
46362
46755
|
);
|
|
46363
46756
|
patchOverlayState = /* @__PURE__ */ __name((next) => $overlayState.set(typeof next === "function" ? next($overlayState.get()) : { ...$overlayState.get(), ...next }), "patchOverlayState");
|
|
46364
46757
|
resetFlowOverlays = /* @__PURE__ */ __name(() => $overlayState.set({
|
|
46365
46758
|
...buildOverlayState(),
|
|
46366
46759
|
agents: $overlayState.get().agents,
|
|
46367
46760
|
agentsInitialHistoryIndex: $overlayState.get().agentsInitialHistoryIndex,
|
|
46761
|
+
agentsWorkflowId: $overlayState.get().agentsWorkflowId,
|
|
46368
46762
|
mcpHub: $overlayState.get().mcpHub,
|
|
46369
46763
|
modelPicker: $overlayState.get().modelPicker,
|
|
46370
46764
|
modelPickerMode: $overlayState.get().modelPickerMode,
|
|
@@ -47164,13 +47558,25 @@ var init_debug = __esm({
|
|
|
47164
47558
|
}
|
|
47165
47559
|
});
|
|
47166
47560
|
|
|
47561
|
+
// src/app/workflowStore.ts
|
|
47562
|
+
import { atom as atom3 } from "nanostores";
|
|
47563
|
+
var $workflowSnapshots, setWorkflowSnapshot;
|
|
47564
|
+
var init_workflowStore = __esm({
|
|
47565
|
+
"src/app/workflowStore.ts"() {
|
|
47566
|
+
"use strict";
|
|
47567
|
+
$workflowSnapshots = atom3({});
|
|
47568
|
+
setWorkflowSnapshot = /* @__PURE__ */ __name((snapshot) => $workflowSnapshots.set({ ...$workflowSnapshots.get(), [snapshot.id]: snapshot }), "setWorkflowSnapshot");
|
|
47569
|
+
}
|
|
47570
|
+
});
|
|
47571
|
+
|
|
47167
47572
|
// src/app/slash/commands/openjaw.ts
|
|
47168
|
-
var fmt, money, stub, eventLine, parseScheduleInput, showUsage, openjawCommands;
|
|
47573
|
+
var fmt, money, stub, eventLine, parseScheduleInput, asWorkflowSnapshot, workflowLine, workflowRows, showUsage, openjawCommands;
|
|
47169
47574
|
var init_openjaw = __esm({
|
|
47170
47575
|
"src/app/slash/commands/openjaw.ts"() {
|
|
47171
47576
|
"use strict";
|
|
47172
47577
|
init_usage();
|
|
47173
47578
|
init_overlayStore();
|
|
47579
|
+
init_workflowStore();
|
|
47174
47580
|
fmt = /* @__PURE__ */ __name((n) => (n ?? 0).toLocaleString(), "fmt");
|
|
47175
47581
|
money = /* @__PURE__ */ __name((n) => `$${(n ?? 0).toFixed(4)}`, "money");
|
|
47176
47582
|
stub = /* @__PURE__ */ __name((ctx, command, message) => {
|
|
@@ -47210,6 +47616,14 @@ var init_openjaw = __esm({
|
|
|
47210
47616
|
}
|
|
47211
47617
|
return null;
|
|
47212
47618
|
}, "parseScheduleInput");
|
|
47619
|
+
asWorkflowSnapshot = /* @__PURE__ */ __name((run) => run ? run : null, "asWorkflowSnapshot");
|
|
47620
|
+
workflowLine = /* @__PURE__ */ __name((run) => {
|
|
47621
|
+
const active = run.workers.filter((worker) => worker.status === "running" || worker.status === "queued").length;
|
|
47622
|
+
const done = run.workers.filter((worker) => worker.status === "completed").length;
|
|
47623
|
+
return `${run.id} \xB7 ${run.status} \xB7 ${done}/${run.workers.length} done${active ? ` \xB7 ${active} active` : ""} \xB7 ${run.goal}`;
|
|
47624
|
+
}, "workflowLine");
|
|
47625
|
+
workflowRows = /* @__PURE__ */ __name((runs) => runs.map((run) => [run.id, `${run.status} \xB7 ${run.workerCount}/${run.plannedWorkerCount} workers
|
|
47626
|
+
${run.goal}`]), "workflowRows");
|
|
47213
47627
|
showUsage = /* @__PURE__ */ __name((ctx, render2) => {
|
|
47214
47628
|
ctx.gateway.rpc("session.usage", { session_id: ctx.sid }).then(ctx.guarded(render2)).catch(ctx.guardedErr);
|
|
47215
47629
|
}, "showUsage");
|
|
@@ -47231,6 +47645,10 @@ var init_openjaw = __esm({
|
|
|
47231
47645
|
if (!r.messages?.length) {
|
|
47232
47646
|
ctx.transcript.sys("connect: no output");
|
|
47233
47647
|
}
|
|
47648
|
+
if (!ctx.sid && r.config_updates) {
|
|
47649
|
+
ctx.transcript.sys("provider connected \u2014 starting OpenJaw session\u2026");
|
|
47650
|
+
ctx.session.newSession();
|
|
47651
|
+
}
|
|
47234
47652
|
})
|
|
47235
47653
|
).catch(ctx.guardedErr);
|
|
47236
47654
|
}, "run")
|
|
@@ -47324,6 +47742,70 @@ var init_openjaw = __esm({
|
|
|
47324
47742
|
).catch(ctx.guardedErr);
|
|
47325
47743
|
}, "run")
|
|
47326
47744
|
},
|
|
47745
|
+
{
|
|
47746
|
+
aliases: ["wf"],
|
|
47747
|
+
help: "start or inspect an advisory dynamic workflow",
|
|
47748
|
+
name: "workflow",
|
|
47749
|
+
usage: "/workflow <goal> | /workflow status [id] | list | show [id] | cancel <id>",
|
|
47750
|
+
run: /* @__PURE__ */ __name((arg, ctx) => {
|
|
47751
|
+
const text = arg.trim();
|
|
47752
|
+
if (!text) {
|
|
47753
|
+
return ctx.transcript.sys("usage: /workflow <goal> | /workflow status [id] | list | show [id] | cancel <id>");
|
|
47754
|
+
}
|
|
47755
|
+
const [subRaw, ...rest] = text.split(/\s+/);
|
|
47756
|
+
const sub = subRaw?.toLowerCase() ?? "";
|
|
47757
|
+
const remainder = rest.join(" ").trim();
|
|
47758
|
+
if (sub === "status") {
|
|
47759
|
+
return ctx.gateway.rpc("workflow.status", { id: remainder, session_id: ctx.sid }).then(
|
|
47760
|
+
ctx.guarded((r) => {
|
|
47761
|
+
const snapshot = asWorkflowSnapshot(r.run);
|
|
47762
|
+
if (!snapshot) return ctx.transcript.sys(r.error || "no workflows yet");
|
|
47763
|
+
setWorkflowSnapshot(snapshot);
|
|
47764
|
+
patchOverlayState({ agents: true, agentsInitialHistoryIndex: 0, agentsWorkflowId: snapshot.id });
|
|
47765
|
+
ctx.transcript.sys(`workflow status \xB7 ${workflowLine(snapshot)}`);
|
|
47766
|
+
})
|
|
47767
|
+
).catch(ctx.guardedErr);
|
|
47768
|
+
}
|
|
47769
|
+
if (sub === "list" || sub === "ls") {
|
|
47770
|
+
return ctx.gateway.rpc("workflow.list", { limit: 30, session_id: ctx.sid }).then(
|
|
47771
|
+
ctx.guarded((r) => {
|
|
47772
|
+
const runs = r.runs ?? [];
|
|
47773
|
+
if (!runs.length) return ctx.transcript.sys("no workflows yet");
|
|
47774
|
+
ctx.transcript.panel("Workflows", [{ rows: workflowRows(runs) }]);
|
|
47775
|
+
})
|
|
47776
|
+
).catch(ctx.guardedErr);
|
|
47777
|
+
}
|
|
47778
|
+
if (sub === "show") {
|
|
47779
|
+
return ctx.gateway.rpc("workflow.show", { id: remainder, session_id: ctx.sid }).then(
|
|
47780
|
+
ctx.guarded((r) => {
|
|
47781
|
+
const snapshot = asWorkflowSnapshot(r.run);
|
|
47782
|
+
if (!snapshot) return ctx.transcript.sys(r.error || "no workflows yet");
|
|
47783
|
+
setWorkflowSnapshot(snapshot);
|
|
47784
|
+
ctx.transcript.page(snapshot.summary || workflowLine(snapshot), `Workflow ${snapshot.id}`);
|
|
47785
|
+
})
|
|
47786
|
+
).catch(ctx.guardedErr);
|
|
47787
|
+
}
|
|
47788
|
+
if (sub === "cancel" || sub === "stop") {
|
|
47789
|
+
if (!remainder) return ctx.transcript.sys("usage: /workflow cancel <runId|workerId>");
|
|
47790
|
+
return ctx.gateway.rpc("workflow.cancel", { id: remainder, session_id: ctx.sid }).then(
|
|
47791
|
+
ctx.guarded((r) => {
|
|
47792
|
+
const snapshot = asWorkflowSnapshot(r.run);
|
|
47793
|
+
if (snapshot) setWorkflowSnapshot(snapshot);
|
|
47794
|
+
ctx.transcript.sys(r.ok ? `workflow cancel requested: ${remainder}` : r.error || `not found: ${remainder}`);
|
|
47795
|
+
})
|
|
47796
|
+
).catch(ctx.guardedErr);
|
|
47797
|
+
}
|
|
47798
|
+
return ctx.gateway.rpc("workflow.start", { goal: text, session_id: ctx.sid }).then(
|
|
47799
|
+
ctx.guarded((r) => {
|
|
47800
|
+
const snapshot = asWorkflowSnapshot(r.run);
|
|
47801
|
+
if (!snapshot) return ctx.transcript.sys(r.error || "workflow failed to start");
|
|
47802
|
+
setWorkflowSnapshot(snapshot);
|
|
47803
|
+
patchOverlayState({ agents: true, agentsInitialHistoryIndex: 0, agentsWorkflowId: snapshot.id });
|
|
47804
|
+
ctx.transcript.sys(`workflow ${snapshot.id} started \xB7 ${snapshot.plannedWorkerCount} workers \xB7 concurrency ${snapshot.concurrency}`);
|
|
47805
|
+
})
|
|
47806
|
+
).catch(ctx.guardedErr);
|
|
47807
|
+
}, "run")
|
|
47808
|
+
},
|
|
47327
47809
|
{
|
|
47328
47810
|
help: "schedule a recurring prompt",
|
|
47329
47811
|
name: "schedule",
|
|
@@ -47456,7 +47938,7 @@ var init_openjaw = __esm({
|
|
|
47456
47938
|
});
|
|
47457
47939
|
|
|
47458
47940
|
// src/app/delegationStore.ts
|
|
47459
|
-
import { atom as
|
|
47941
|
+
import { atom as atom4 } from "nanostores";
|
|
47460
47942
|
var buildState, $delegationState, getDelegationState, patchDelegationState, $overlaySectionsOpen, toggleOverlaySection, applyDelegationStatus;
|
|
47461
47943
|
var init_delegationStore = __esm({
|
|
47462
47944
|
"src/app/delegationStore.ts"() {
|
|
@@ -47467,10 +47949,10 @@ var init_delegationStore = __esm({
|
|
|
47467
47949
|
paused: false,
|
|
47468
47950
|
updatedAt: null
|
|
47469
47951
|
}), "buildState");
|
|
47470
|
-
$delegationState =
|
|
47952
|
+
$delegationState = atom4(buildState());
|
|
47471
47953
|
getDelegationState = /* @__PURE__ */ __name(() => $delegationState.get(), "getDelegationState");
|
|
47472
47954
|
patchDelegationState = /* @__PURE__ */ __name((next) => $delegationState.set({ ...$delegationState.get(), ...next }), "patchDelegationState");
|
|
47473
|
-
$overlaySectionsOpen =
|
|
47955
|
+
$overlaySectionsOpen = atom4({});
|
|
47474
47956
|
toggleOverlaySection = /* @__PURE__ */ __name((title, defaultOpen) => {
|
|
47475
47957
|
const state = $overlaySectionsOpen.get();
|
|
47476
47958
|
const current = title in state ? state[title] : defaultOpen;
|
|
@@ -47496,7 +47978,7 @@ var init_delegationStore = __esm({
|
|
|
47496
47978
|
});
|
|
47497
47979
|
|
|
47498
47980
|
// src/app/spawnHistoryStore.ts
|
|
47499
|
-
import { atom as
|
|
47981
|
+
import { atom as atom5 } from "nanostores";
|
|
47500
47982
|
function summarizeLabel(subagents) {
|
|
47501
47983
|
const top = subagents.filter((s) => s.parentId == null || subagents.every((o) => o.id !== s.parentId)).slice(0, 2).map((s) => s.goal || "subagent").join(" \xB7 ");
|
|
47502
47984
|
return top || `${subagents.length} agent${subagents.length === 1 ? "" : "s"}`;
|
|
@@ -47539,8 +48021,8 @@ var init_spawnHistoryStore = __esm({
|
|
|
47539
48021
|
"src/app/spawnHistoryStore.ts"() {
|
|
47540
48022
|
"use strict";
|
|
47541
48023
|
HISTORY_LIMIT = 10;
|
|
47542
|
-
$spawnHistory =
|
|
47543
|
-
$spawnDiff =
|
|
48024
|
+
$spawnHistory = atom5([]);
|
|
48025
|
+
$spawnDiff = atom5(null);
|
|
47544
48026
|
getSpawnHistory = /* @__PURE__ */ __name(() => $spawnHistory.get(), "getSpawnHistory");
|
|
47545
48027
|
clearDiffPair = /* @__PURE__ */ __name(() => $spawnDiff.set(null), "clearDiffPair");
|
|
47546
48028
|
setDiffPair = /* @__PURE__ */ __name((pair) => $spawnDiff.set(pair), "setDiffPair");
|
|
@@ -47850,15 +48332,15 @@ var init_ops = __esm({
|
|
|
47850
48332
|
}
|
|
47851
48333
|
const [a, b] = parts;
|
|
47852
48334
|
const history = getSpawnHistory();
|
|
47853
|
-
const
|
|
48335
|
+
const resolve6 = /* @__PURE__ */ __name((token) => {
|
|
47854
48336
|
const n = parseInt(token, 10);
|
|
47855
48337
|
if (Number.isFinite(n) && n >= 1 && n <= history.length) {
|
|
47856
48338
|
return history[n - 1] ?? null;
|
|
47857
48339
|
}
|
|
47858
48340
|
return null;
|
|
47859
48341
|
}, "resolve");
|
|
47860
|
-
const baseline =
|
|
47861
|
-
const candidate =
|
|
48342
|
+
const baseline = resolve6(a);
|
|
48343
|
+
const candidate = resolve6(b);
|
|
47862
48344
|
if (!baseline || !candidate) {
|
|
47863
48345
|
return ctx.transcript.sys(`replay-diff: could not resolve indices \xB7 history has ${history.length} entries`);
|
|
47864
48346
|
}
|
|
@@ -48731,76 +49213,20 @@ var init_session2 = __esm({
|
|
|
48731
49213
|
}
|
|
48732
49214
|
});
|
|
48733
49215
|
|
|
48734
|
-
// src/lib/externalCli.ts
|
|
48735
|
-
import { spawn as spawn6 } from "node:child_process";
|
|
48736
|
-
var resolveHermesBin, launchHermesCommand;
|
|
48737
|
-
var init_externalCli = __esm({
|
|
48738
|
-
"src/lib/externalCli.ts"() {
|
|
48739
|
-
"use strict";
|
|
48740
|
-
resolveHermesBin = /* @__PURE__ */ __name(() => process.env.OPENJAW_BIN?.trim() || "hermes", "resolveHermesBin");
|
|
48741
|
-
launchHermesCommand = /* @__PURE__ */ __name((args) => new Promise((resolve5) => {
|
|
48742
|
-
const child = spawn6(resolveHermesBin(), args, { stdio: "inherit" });
|
|
48743
|
-
child.on("error", (err) => resolve5({ code: null, error: err.message }));
|
|
48744
|
-
child.on("exit", (code) => resolve5({ code }));
|
|
48745
|
-
}), "launchHermesCommand");
|
|
48746
|
-
}
|
|
48747
|
-
});
|
|
48748
|
-
|
|
48749
|
-
// src/app/setupHandoff.ts
|
|
48750
|
-
async function runExternalSetup({ args, ctx, done, launcher, suspend }) {
|
|
48751
|
-
const { gateway, session, transcript } = ctx;
|
|
48752
|
-
transcript.sys(`launching \`hermes ${args.join(" ")}\`\u2026`);
|
|
48753
|
-
patchUiState({ status: "setup running\u2026" });
|
|
48754
|
-
let result = { code: null };
|
|
48755
|
-
await suspend(async () => {
|
|
48756
|
-
result = await launcher(args);
|
|
48757
|
-
});
|
|
48758
|
-
if (result.error) {
|
|
48759
|
-
transcript.sys(`error launching hermes: ${result.error}`);
|
|
48760
|
-
patchUiState({ status: "setup required" });
|
|
48761
|
-
return;
|
|
48762
|
-
}
|
|
48763
|
-
if (result.code !== 0) {
|
|
48764
|
-
transcript.sys(`hermes ${args[0]} exited with code ${result.code}`);
|
|
48765
|
-
patchUiState({ status: "setup required" });
|
|
48766
|
-
return;
|
|
48767
|
-
}
|
|
48768
|
-
const setup = await gateway.rpc("setup.status", {});
|
|
48769
|
-
if (setup?.provider_configured === false) {
|
|
48770
|
-
transcript.sys("still no provider configured");
|
|
48771
|
-
patchUiState({ status: "setup required" });
|
|
48772
|
-
return;
|
|
48773
|
-
}
|
|
48774
|
-
transcript.sys(done);
|
|
48775
|
-
session.newSession();
|
|
48776
|
-
}
|
|
48777
|
-
var init_setupHandoff = __esm({
|
|
48778
|
-
"src/app/setupHandoff.ts"() {
|
|
48779
|
-
"use strict";
|
|
48780
|
-
init_uiStore();
|
|
48781
|
-
__name(runExternalSetup, "runExternalSetup");
|
|
48782
|
-
}
|
|
48783
|
-
});
|
|
48784
|
-
|
|
48785
49216
|
// src/app/slash/commands/setup.ts
|
|
48786
49217
|
var setupCommands;
|
|
48787
49218
|
var init_setup = __esm({
|
|
48788
49219
|
"src/app/slash/commands/setup.ts"() {
|
|
48789
49220
|
"use strict";
|
|
48790
|
-
|
|
48791
|
-
init_externalCli();
|
|
48792
|
-
init_setupHandoff();
|
|
49221
|
+
init_overlayStore();
|
|
48793
49222
|
setupCommands = [
|
|
48794
49223
|
{
|
|
48795
|
-
help: "run
|
|
49224
|
+
help: "first-run setup help; opens provider connection picker",
|
|
48796
49225
|
name: "setup",
|
|
48797
|
-
run: /* @__PURE__ */ __name((
|
|
48798
|
-
|
|
48799
|
-
|
|
48800
|
-
|
|
48801
|
-
launcher: launchHermesCommand,
|
|
48802
|
-
suspend: withInkSuspended
|
|
48803
|
-
}), "run")
|
|
49226
|
+
run: /* @__PURE__ */ __name((_arg, ctx) => {
|
|
49227
|
+
ctx.transcript.sys("OpenJaw setup: run /connect to set up a provider, then /model to choose a model.");
|
|
49228
|
+
patchOverlayState({ modelPicker: true, modelPickerMode: "connect" });
|
|
49229
|
+
}, "run")
|
|
48804
49230
|
}
|
|
48805
49231
|
];
|
|
48806
49232
|
}
|
|
@@ -48956,17 +49382,856 @@ var init_catalog = __esm({
|
|
|
48956
49382
|
}
|
|
48957
49383
|
});
|
|
48958
49384
|
|
|
48959
|
-
// src/
|
|
48960
|
-
|
|
48961
|
-
|
|
48962
|
-
|
|
49385
|
+
// src/workflows/planner.ts
|
|
49386
|
+
function resolveDynamicWorkflowConfig(config) {
|
|
49387
|
+
const raw = config.features?.dynamic_workflows ?? {};
|
|
49388
|
+
return {
|
|
49389
|
+
enabled: raw.enabled ?? DEFAULT_DYNAMIC_WORKFLOW_CONFIG.enabled,
|
|
49390
|
+
plannerMode: "adaptive",
|
|
49391
|
+
hardMaxWorkers: clampInt(raw.hard_max_workers, DEFAULT_DYNAMIC_WORKFLOW_CONFIG.hardMaxWorkers, 1, 1e4),
|
|
49392
|
+
hardMaxConcurrentWorkers: clampInt(
|
|
49393
|
+
raw.hard_max_concurrent_workers,
|
|
49394
|
+
DEFAULT_DYNAMIC_WORKFLOW_CONFIG.hardMaxConcurrentWorkers,
|
|
49395
|
+
1,
|
|
49396
|
+
2048
|
|
49397
|
+
),
|
|
49398
|
+
workerTimeoutMs: clampInt(raw.worker_timeout_ms, DEFAULT_DYNAMIC_WORKFLOW_CONFIG.workerTimeoutMs, 1e4, 36e5),
|
|
49399
|
+
persistHistory: raw.persist_history ?? DEFAULT_DYNAMIC_WORKFLOW_CONFIG.persistHistory
|
|
49400
|
+
};
|
|
49401
|
+
}
|
|
49402
|
+
function initialConcurrency(plannedWorkers, limits) {
|
|
49403
|
+
if (plannedWorkers <= 0) return 0;
|
|
49404
|
+
return Math.min(Math.ceil(Math.sqrt(plannedWorkers) * 2), plannedWorkers, limits.hardMaxConcurrentWorkers);
|
|
49405
|
+
}
|
|
49406
|
+
function adjustConcurrency(current, plannedWorkers, limits, event) {
|
|
49407
|
+
if (plannedWorkers <= 0) return 0;
|
|
49408
|
+
if (event === "rate_limited") return Math.max(1, Math.floor(current / 2));
|
|
49409
|
+
return Math.min(current + 1, plannedWorkers, limits.hardMaxConcurrentWorkers);
|
|
49410
|
+
}
|
|
49411
|
+
function planWorkflow(goal, runId, limits) {
|
|
49412
|
+
const taskGoals = deriveTaskGoals(goal);
|
|
49413
|
+
const specs = [];
|
|
49414
|
+
const maxWorkers = limits.hardMaxWorkers;
|
|
49415
|
+
for (const taskGoal of taskGoals) {
|
|
49416
|
+
if (specs.length >= maxWorkers) break;
|
|
49417
|
+
const id = `${runId}-w${specs.length + 1}`;
|
|
49418
|
+
specs.push({
|
|
49419
|
+
depth: 0,
|
|
49420
|
+
goal: taskGoal,
|
|
49421
|
+
id,
|
|
49422
|
+
index: specs.length,
|
|
49423
|
+
parentId: null,
|
|
49424
|
+
prompt: buildTaskPrompt(goal, taskGoal),
|
|
49425
|
+
role: "task"
|
|
49426
|
+
});
|
|
49427
|
+
}
|
|
49428
|
+
const taskSpecs = [...specs];
|
|
49429
|
+
const verifierEvery = taskSpecs.length >= 8 ? 4 : 3;
|
|
49430
|
+
for (let i = 0; i < taskSpecs.length && specs.length < maxWorkers; i += verifierEvery) {
|
|
49431
|
+
const batch = taskSpecs.slice(i, i + verifierEvery);
|
|
49432
|
+
const id = `${runId}-w${specs.length + 1}`;
|
|
49433
|
+
specs.push({
|
|
49434
|
+
depth: 1,
|
|
49435
|
+
dependsOn: batch.map((item) => item.id),
|
|
49436
|
+
goal: `Verify findings from workers ${batch.map((item) => item.index + 1).join(", ")}`,
|
|
49437
|
+
id,
|
|
49438
|
+
index: specs.length,
|
|
49439
|
+
parentId: batch[0]?.id ?? null,
|
|
49440
|
+
prompt: buildVerifierPrompt(goal, batch),
|
|
49441
|
+
role: "verifier"
|
|
49442
|
+
});
|
|
49443
|
+
}
|
|
49444
|
+
return { concurrency: initialConcurrency(specs.length, limits), specs };
|
|
49445
|
+
}
|
|
49446
|
+
function deriveTaskGoals(goal) {
|
|
49447
|
+
const explicit = extractExplicitTasks(goal);
|
|
49448
|
+
const words = goal.trim().split(/\s+/).filter(Boolean).length;
|
|
49449
|
+
const complexity = Math.min(12, Math.max(3, Math.ceil(words / 35)));
|
|
49450
|
+
const inferred = inferLanes(goal);
|
|
49451
|
+
const seeds = explicit.length > 1 ? explicit : inferred;
|
|
49452
|
+
const out = [];
|
|
49453
|
+
for (const item of seeds) {
|
|
49454
|
+
pushUnique(out, item);
|
|
49455
|
+
}
|
|
49456
|
+
while (out.length < complexity) {
|
|
49457
|
+
const next = DEFAULT_LANES[out.length % DEFAULT_LANES.length];
|
|
49458
|
+
pushUnique(out, next);
|
|
49459
|
+
if (out.length >= DEFAULT_LANES.length && explicit.length <= 1) break;
|
|
49460
|
+
}
|
|
49461
|
+
return out.slice(0, Math.max(1, out.length));
|
|
49462
|
+
}
|
|
49463
|
+
function extractExplicitTasks(goal) {
|
|
49464
|
+
return goal.split(/\r?\n/).map((line) => line.trim().replace(/^[-*+]\s+/, "").replace(/^\d+[.)]\s+/, "").trim()).filter((line) => line.length >= 12);
|
|
49465
|
+
}
|
|
49466
|
+
function inferLanes(goal) {
|
|
49467
|
+
const lower = goal.toLowerCase();
|
|
49468
|
+
const lanes = ["Map the current state and locate relevant files or evidence"];
|
|
49469
|
+
if (/code|repo|branch|test|build|bug|feature|implement/.test(lower)) {
|
|
49470
|
+
lanes.push("Review implementation risks, edge cases, and integration points");
|
|
49471
|
+
lanes.push("Inspect validation strategy and likely test coverage");
|
|
49472
|
+
}
|
|
49473
|
+
if (/research|latest|current|external|claude|compare|similar/.test(lower)) {
|
|
49474
|
+
lanes.push("Research external context and comparable workflow behavior");
|
|
49475
|
+
}
|
|
49476
|
+
if (/ui|status|overlay|navigate|progress|worker/.test(lower)) {
|
|
49477
|
+
lanes.push("Evaluate user experience, status visibility, and navigation requirements");
|
|
49478
|
+
}
|
|
49479
|
+
if (/security|safe|permission|mutat|write|approval/.test(lower)) {
|
|
49480
|
+
lanes.push("Analyze safety boundaries, permissions, and failure modes");
|
|
49481
|
+
}
|
|
49482
|
+
lanes.push("Draft concrete recommendations and acceptance criteria");
|
|
49483
|
+
return lanes;
|
|
49484
|
+
}
|
|
49485
|
+
function pushUnique(items, value) {
|
|
49486
|
+
const normalized = value.trim();
|
|
49487
|
+
if (!normalized) return;
|
|
49488
|
+
if (!items.some((item) => item.toLowerCase() === normalized.toLowerCase())) items.push(normalized);
|
|
49489
|
+
}
|
|
49490
|
+
function buildTaskPrompt(overallGoal, taskGoal) {
|
|
49491
|
+
return [
|
|
49492
|
+
`Overall workflow goal: ${overallGoal}`,
|
|
49493
|
+
`Your assigned read-only worker goal: ${taskGoal}`,
|
|
49494
|
+
"",
|
|
49495
|
+
"Work independently. Use only read/search/analysis tools. Do not modify files, send messages, update memory, or run shell/code execution.",
|
|
49496
|
+
"Return concise findings with evidence, confidence, and open questions."
|
|
49497
|
+
].join("\n");
|
|
49498
|
+
}
|
|
49499
|
+
function buildVerifierPrompt(overallGoal, batch) {
|
|
49500
|
+
return [
|
|
49501
|
+
`Overall workflow goal: ${overallGoal}`,
|
|
49502
|
+
`Verify the likely claims and gaps for these worker lanes: ${batch.map((item) => item.goal).join("; ")}`,
|
|
49503
|
+
"",
|
|
49504
|
+
"Use read-only checks. Label findings as verified, partially verified, or unverified. Do not modify anything."
|
|
49505
|
+
].join("\n");
|
|
49506
|
+
}
|
|
49507
|
+
var DEFAULT_DYNAMIC_WORKFLOW_CONFIG, clampInt, DEFAULT_LANES;
|
|
49508
|
+
var init_planner = __esm({
|
|
49509
|
+
"src/workflows/planner.ts"() {
|
|
49510
|
+
"use strict";
|
|
49511
|
+
DEFAULT_DYNAMIC_WORKFLOW_CONFIG = {
|
|
49512
|
+
enabled: true,
|
|
49513
|
+
plannerMode: "adaptive",
|
|
49514
|
+
hardMaxWorkers: 1024,
|
|
49515
|
+
hardMaxConcurrentWorkers: 128,
|
|
49516
|
+
workerTimeoutMs: 18e4,
|
|
49517
|
+
persistHistory: true
|
|
49518
|
+
};
|
|
49519
|
+
clampInt = /* @__PURE__ */ __name((value, fallback, min, max) => {
|
|
49520
|
+
const parsed = typeof value === "number" ? value : Number(value);
|
|
49521
|
+
if (!Number.isFinite(parsed)) return fallback;
|
|
49522
|
+
return Math.min(max, Math.max(min, Math.floor(parsed)));
|
|
49523
|
+
}, "clampInt");
|
|
49524
|
+
__name(resolveDynamicWorkflowConfig, "resolveDynamicWorkflowConfig");
|
|
49525
|
+
__name(initialConcurrency, "initialConcurrency");
|
|
49526
|
+
__name(adjustConcurrency, "adjustConcurrency");
|
|
49527
|
+
__name(planWorkflow, "planWorkflow");
|
|
49528
|
+
__name(deriveTaskGoals, "deriveTaskGoals");
|
|
49529
|
+
__name(extractExplicitTasks, "extractExplicitTasks");
|
|
49530
|
+
__name(inferLanes, "inferLanes");
|
|
49531
|
+
DEFAULT_LANES = [
|
|
49532
|
+
"Map the current state and locate relevant files or evidence",
|
|
49533
|
+
"Review implementation risks, edge cases, and integration points",
|
|
49534
|
+
"Inspect validation strategy and likely test coverage",
|
|
49535
|
+
"Analyze safety boundaries, permissions, and failure modes",
|
|
49536
|
+
"Draft concrete recommendations and acceptance criteria"
|
|
49537
|
+
];
|
|
49538
|
+
__name(pushUnique, "pushUnique");
|
|
49539
|
+
__name(buildTaskPrompt, "buildTaskPrompt");
|
|
49540
|
+
__name(buildVerifierPrompt, "buildVerifierPrompt");
|
|
49541
|
+
}
|
|
49542
|
+
});
|
|
49543
|
+
|
|
49544
|
+
// src/workflows/persistence.ts
|
|
49545
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync17, readdirSync as readdirSync7, readFileSync as readFileSync28, writeFileSync as writeFileSync19 } from "node:fs";
|
|
48963
49546
|
import { homedir as homedir28 } from "node:os";
|
|
48964
|
-
import { basename as basename3,
|
|
49547
|
+
import { basename as basename3, join as join42, resolve as resolve4 } from "node:path";
|
|
49548
|
+
function ensureDir(path3) {
|
|
49549
|
+
if (!existsSync31(path3)) mkdirSync17(path3, { recursive: true });
|
|
49550
|
+
}
|
|
49551
|
+
function safeSegment(value) {
|
|
49552
|
+
return value.replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 80) || "default";
|
|
49553
|
+
}
|
|
49554
|
+
function readJson(path3) {
|
|
49555
|
+
try {
|
|
49556
|
+
return JSON.parse(readFileSync28(path3, "utf8"));
|
|
49557
|
+
} catch {
|
|
49558
|
+
return null;
|
|
49559
|
+
}
|
|
49560
|
+
}
|
|
49561
|
+
function saveWorkflowSnapshot(snapshot) {
|
|
49562
|
+
ensureDir(workflowDir());
|
|
49563
|
+
const path3 = join42(workflowDir(), `${safeSegment(snapshot.id)}.json`);
|
|
49564
|
+
writeFileSync19(path3, `${JSON.stringify(snapshot, null, 2)}
|
|
49565
|
+
`, "utf8");
|
|
49566
|
+
return path3;
|
|
49567
|
+
}
|
|
49568
|
+
function loadWorkflowSnapshot(id) {
|
|
49569
|
+
const path3 = join42(workflowDir(), `${safeSegment(id)}.json`);
|
|
49570
|
+
return readJson(path3);
|
|
49571
|
+
}
|
|
49572
|
+
function listWorkflowSnapshots(limit = 30) {
|
|
49573
|
+
if (!existsSync31(workflowDir())) return [];
|
|
49574
|
+
const entries = [];
|
|
49575
|
+
for (const entry of readdirSync7(workflowDir(), { withFileTypes: true })) {
|
|
49576
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
49577
|
+
const path3 = join42(workflowDir(), entry.name);
|
|
49578
|
+
const snapshot = readJson(path3);
|
|
49579
|
+
if (!snapshot) continue;
|
|
49580
|
+
entries.push({
|
|
49581
|
+
finishedAt: snapshot.finishedAt,
|
|
49582
|
+
goal: snapshot.goal,
|
|
49583
|
+
id: snapshot.id,
|
|
49584
|
+
path: path3,
|
|
49585
|
+
plannedWorkerCount: snapshot.plannedWorkerCount,
|
|
49586
|
+
startedAt: snapshot.startedAt,
|
|
49587
|
+
status: snapshot.status,
|
|
49588
|
+
workerCount: snapshot.workers.length
|
|
49589
|
+
});
|
|
49590
|
+
}
|
|
49591
|
+
return entries.sort((a, b) => (b.finishedAt ?? b.startedAt) - (a.finishedAt ?? a.startedAt)).slice(0, Math.max(1, limit));
|
|
49592
|
+
}
|
|
49593
|
+
function saveSpawnTreeSnapshot(input) {
|
|
49594
|
+
const sessionId = safeSegment(input.session_id ?? "default");
|
|
49595
|
+
const dir2 = join42(spawnTreeDir(), sessionId);
|
|
49596
|
+
ensureDir(dir2);
|
|
49597
|
+
const stamp = new Date((input.finished_at ?? Date.now() / 1e3) * 1e3).toISOString().replace(/[:.]/g, "-");
|
|
49598
|
+
const path3 = join42(dir2, `${stamp}-${Math.random().toString(36).slice(2, 8)}.json`);
|
|
49599
|
+
const snapshot = {
|
|
49600
|
+
count: input.subagents?.length ?? 0,
|
|
49601
|
+
finished_at: input.finished_at,
|
|
49602
|
+
label: input.label,
|
|
49603
|
+
session_id: input.session_id,
|
|
49604
|
+
started_at: input.started_at,
|
|
49605
|
+
subagents: input.subagents ?? []
|
|
49606
|
+
};
|
|
49607
|
+
writeFileSync19(path3, `${JSON.stringify(snapshot, null, 2)}
|
|
49608
|
+
`, "utf8");
|
|
49609
|
+
return path3;
|
|
49610
|
+
}
|
|
49611
|
+
function listSpawnTreeSnapshots(sessionId = "default", limit = 30) {
|
|
49612
|
+
const dir2 = join42(spawnTreeDir(), safeSegment(sessionId));
|
|
49613
|
+
if (!existsSync31(dir2)) return [];
|
|
49614
|
+
const entries = [];
|
|
49615
|
+
for (const entry of readdirSync7(dir2, { withFileTypes: true })) {
|
|
49616
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
49617
|
+
const path3 = join42(dir2, entry.name);
|
|
49618
|
+
const snapshot = readJson(path3);
|
|
49619
|
+
if (!snapshot) continue;
|
|
49620
|
+
entries.push({
|
|
49621
|
+
count: snapshot.count,
|
|
49622
|
+
finished_at: snapshot.finished_at,
|
|
49623
|
+
label: snapshot.label,
|
|
49624
|
+
path: path3,
|
|
49625
|
+
session_id: snapshot.session_id,
|
|
49626
|
+
started_at: snapshot.started_at
|
|
49627
|
+
});
|
|
49628
|
+
}
|
|
49629
|
+
return entries.sort((a, b) => (b.finished_at ?? 0) - (a.finished_at ?? 0)).slice(0, Math.max(1, limit));
|
|
49630
|
+
}
|
|
49631
|
+
function loadSpawnTreeSnapshot(path3) {
|
|
49632
|
+
const root = resolve4(spawnTreeDir());
|
|
49633
|
+
const resolved = resolve4(path3);
|
|
49634
|
+
if (!resolved.startsWith(root)) return null;
|
|
49635
|
+
const snapshot = readJson(resolved);
|
|
49636
|
+
return snapshot ? { ...snapshot, path: resolved } : null;
|
|
49637
|
+
}
|
|
49638
|
+
var rootDir, workflowDir, spawnTreeDir;
|
|
49639
|
+
var init_persistence = __esm({
|
|
49640
|
+
"src/workflows/persistence.ts"() {
|
|
49641
|
+
"use strict";
|
|
49642
|
+
rootDir = /* @__PURE__ */ __name(() => join42(homedir28(), ".openjaw-agent"), "rootDir");
|
|
49643
|
+
workflowDir = /* @__PURE__ */ __name(() => join42(rootDir(), "workflows"), "workflowDir");
|
|
49644
|
+
spawnTreeDir = /* @__PURE__ */ __name(() => join42(rootDir(), "spawn-trees"), "spawnTreeDir");
|
|
49645
|
+
__name(ensureDir, "ensureDir");
|
|
49646
|
+
__name(safeSegment, "safeSegment");
|
|
49647
|
+
__name(readJson, "readJson");
|
|
49648
|
+
__name(saveWorkflowSnapshot, "saveWorkflowSnapshot");
|
|
49649
|
+
__name(loadWorkflowSnapshot, "loadWorkflowSnapshot");
|
|
49650
|
+
__name(listWorkflowSnapshots, "listWorkflowSnapshots");
|
|
49651
|
+
__name(saveSpawnTreeSnapshot, "saveSpawnTreeSnapshot");
|
|
49652
|
+
__name(listSpawnTreeSnapshots, "listSpawnTreeSnapshots");
|
|
49653
|
+
__name(loadSpawnTreeSnapshot, "loadSpawnTreeSnapshot");
|
|
49654
|
+
}
|
|
49655
|
+
});
|
|
49656
|
+
|
|
49657
|
+
// src/workflows/readOnlyTools.ts
|
|
49658
|
+
function isWorkflowReadOnlyTool(name) {
|
|
49659
|
+
return READ_ONLY_TOOLS.has(name);
|
|
49660
|
+
}
|
|
49661
|
+
var READ_ONLY_TOOLS, ReadOnlyToolRuntime;
|
|
49662
|
+
var init_readOnlyTools = __esm({
|
|
49663
|
+
"src/workflows/readOnlyTools.ts"() {
|
|
49664
|
+
"use strict";
|
|
49665
|
+
READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
49666
|
+
"browser_snapshot",
|
|
49667
|
+
"browser_extract",
|
|
49668
|
+
"file_info",
|
|
49669
|
+
"file_list",
|
|
49670
|
+
"file_read",
|
|
49671
|
+
"glob",
|
|
49672
|
+
"grep",
|
|
49673
|
+
"image_view",
|
|
49674
|
+
"web_extract",
|
|
49675
|
+
"web_fetch",
|
|
49676
|
+
"web_search"
|
|
49677
|
+
]);
|
|
49678
|
+
__name(isWorkflowReadOnlyTool, "isWorkflowReadOnlyTool");
|
|
49679
|
+
ReadOnlyToolRuntime = class {
|
|
49680
|
+
constructor(inner) {
|
|
49681
|
+
this.inner = inner;
|
|
49682
|
+
}
|
|
49683
|
+
inner;
|
|
49684
|
+
static {
|
|
49685
|
+
__name(this, "ReadOnlyToolRuntime");
|
|
49686
|
+
}
|
|
49687
|
+
listTools() {
|
|
49688
|
+
return this.inner.listTools().filter((tool) => isWorkflowReadOnlyTool(tool.name));
|
|
49689
|
+
}
|
|
49690
|
+
async execute(name, input) {
|
|
49691
|
+
if (!isWorkflowReadOnlyTool(name)) {
|
|
49692
|
+
throw new Error(`Tool ${name} is not available in advisory workflow workers`);
|
|
49693
|
+
}
|
|
49694
|
+
return await this.inner.execute(name, input);
|
|
49695
|
+
}
|
|
49696
|
+
};
|
|
49697
|
+
}
|
|
49698
|
+
});
|
|
49699
|
+
|
|
49700
|
+
// src/workflows/manager.ts
|
|
49701
|
+
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
49702
|
+
function buildPlannerSystemPrompt(limits) {
|
|
49703
|
+
return [
|
|
49704
|
+
"You are the OpenJaw dynamic workflow planner.",
|
|
49705
|
+
"Return ONLY valid JSON. Do not include markdown fences or prose.",
|
|
49706
|
+
"Create a task graph for advisory read-only worker agents. Do not answer the user task.",
|
|
49707
|
+
"Use enough workers to represent genuinely different research lanes, viewpoints, critics, verifiers, and a final synthesizer. Do not default to a small fixed count.",
|
|
49708
|
+
"For panel-review prompts, create separate panelist workers for each requested philosophy and a critic/risk worker before the synthesizer.",
|
|
49709
|
+
`Physical caps: at most ${limits.hardMaxWorkers} total workers and ${limits.hardMaxConcurrentWorkers} concurrent workers. Normal plans should be smaller than the cap but may contain dozens of workers when useful.`,
|
|
49710
|
+
'Schema: {"language":"optional output language","workers":[{"id":"kebab-id","role":"researcher|panelist|critic|verifier|synthesizer","philosophy":"optional","goal":"specific worker goal","depends_on":["worker-id"]}]}',
|
|
49711
|
+
"Always include exactly one synthesizer worker that depends on the most important research, panelist, critic, and verifier workers."
|
|
49712
|
+
].join("\n");
|
|
49713
|
+
}
|
|
49714
|
+
function parseModelPlan(content, goal, runId, limits) {
|
|
49715
|
+
const parsed = JSON.parse(extractJsonObject(content));
|
|
49716
|
+
const workers = Array.isArray(parsed.workers) ? parsed.workers : [];
|
|
49717
|
+
const specs = [];
|
|
49718
|
+
const usedIds = /* @__PURE__ */ new Set();
|
|
49719
|
+
for (const worker of workers.slice(0, limits.hardMaxWorkers)) {
|
|
49720
|
+
const rawGoal = String(worker.goal ?? "").trim();
|
|
49721
|
+
if (!rawGoal) continue;
|
|
49722
|
+
const id = uniqueWorkerId(runId, worker.id || rawGoal, usedIds, specs.length + 1);
|
|
49723
|
+
const role = normalizeRole(worker.role);
|
|
49724
|
+
const dependsOn = Array.isArray(worker.depends_on) ? worker.depends_on.map(String).map((value) => scopedWorkerId(runId, value)).filter((value) => usedIds.has(value)) : [];
|
|
49725
|
+
specs.push({
|
|
49726
|
+
depth: dependsOn.length > 0 || role !== "task" ? 1 : 0,
|
|
49727
|
+
dependsOn,
|
|
49728
|
+
goal: rawGoal,
|
|
49729
|
+
id,
|
|
49730
|
+
index: specs.length,
|
|
49731
|
+
parentId: dependsOn[0] ?? null,
|
|
49732
|
+
prompt: buildModelWorkerPrompt(goal, rawGoal, worker.role, worker.philosophy, parsed.language),
|
|
49733
|
+
role
|
|
49734
|
+
});
|
|
49735
|
+
}
|
|
49736
|
+
return ensureSynthesizer({ concurrency: 0, specs }, goal, runId, limits);
|
|
49737
|
+
}
|
|
49738
|
+
function ensureSynthesizer(plan, goal, runId, limits) {
|
|
49739
|
+
const specs = plan.specs.slice(0, limits.hardMaxWorkers);
|
|
49740
|
+
const existing = specs.find((spec) => spec.role === "synthesizer");
|
|
49741
|
+
const dependencies = specs.filter((spec) => spec.role !== "synthesizer").map((spec) => spec.id);
|
|
49742
|
+
if (existing) {
|
|
49743
|
+
existing.dependsOn = existing.dependsOn?.length ? existing.dependsOn : dependencies;
|
|
49744
|
+
existing.depth = Math.max(existing.depth, 1);
|
|
49745
|
+
existing.parentId = existing.dependsOn[0] ?? null;
|
|
49746
|
+
} else if (specs.length < limits.hardMaxWorkers) {
|
|
49747
|
+
specs.push({
|
|
49748
|
+
depth: 1,
|
|
49749
|
+
dependsOn: dependencies,
|
|
49750
|
+
goal: "Synthesize the panel and research outputs into the final answer",
|
|
49751
|
+
id: `${runId}-synthesizer`,
|
|
49752
|
+
index: specs.length,
|
|
49753
|
+
parentId: dependencies[0] ?? null,
|
|
49754
|
+
prompt: buildModelWorkerPrompt(goal, "Synthesize all completed worker outputs into the final answer for the user.", "synthesizer", void 0, void 0),
|
|
49755
|
+
role: "synthesizer"
|
|
49756
|
+
});
|
|
49757
|
+
}
|
|
49758
|
+
return { concurrency: Math.min(Math.ceil(Math.sqrt(specs.length) * 2), specs.length, limits.hardMaxConcurrentWorkers), specs };
|
|
49759
|
+
}
|
|
49760
|
+
function buildModelWorkerPrompt(overallGoal, workerGoal, role, philosophy, language) {
|
|
49761
|
+
return [
|
|
49762
|
+
`Overall workflow goal: ${overallGoal}`,
|
|
49763
|
+
`Worker role: ${role || "researcher"}`,
|
|
49764
|
+
philosophy ? `Investment/work philosophy: ${philosophy}` : "",
|
|
49765
|
+
language ? `Final language preference: ${language}` : "",
|
|
49766
|
+
`Assigned goal: ${workerGoal}`,
|
|
49767
|
+
"",
|
|
49768
|
+
"Use only read/search/analysis tools. Do not modify files, send messages, update memory, or run shell/code execution.",
|
|
49769
|
+
"If prior worker outputs are provided below, use them as context and explicitly resolve agreements, disagreements, and uncertainty.",
|
|
49770
|
+
"Return evidence-backed findings. A synthesizer must produce the final answer directly for the user."
|
|
49771
|
+
].filter(Boolean).join("\n");
|
|
49772
|
+
}
|
|
49773
|
+
function normalizeRole(role) {
|
|
49774
|
+
const lower = String(role ?? "").toLowerCase();
|
|
49775
|
+
if (lower.includes("synth") || lower.includes("chair")) return "synthesizer";
|
|
49776
|
+
if (lower.includes("verify") || lower.includes("critic") || lower.includes("risk")) return "verifier";
|
|
49777
|
+
return "task";
|
|
49778
|
+
}
|
|
49779
|
+
function extractJsonObject(content) {
|
|
49780
|
+
const trimmed = content.trim().replace(/^```(?:json)?\s*/i, "").replace(/```$/i, "").trim();
|
|
49781
|
+
const start = trimmed.indexOf("{");
|
|
49782
|
+
const end = trimmed.lastIndexOf("}");
|
|
49783
|
+
if (start < 0 || end < start) throw new Error("planner response did not contain a JSON object");
|
|
49784
|
+
return trimmed.slice(start, end + 1);
|
|
49785
|
+
}
|
|
49786
|
+
function uniqueWorkerId(runId, value, used, index) {
|
|
49787
|
+
const base = scopedWorkerId(runId, value) || `${runId}-w${index}`;
|
|
49788
|
+
let candidate = base;
|
|
49789
|
+
let suffix = 2;
|
|
49790
|
+
while (used.has(candidate)) {
|
|
49791
|
+
candidate = `${base}-${suffix}`;
|
|
49792
|
+
suffix += 1;
|
|
49793
|
+
}
|
|
49794
|
+
used.add(candidate);
|
|
49795
|
+
return candidate;
|
|
49796
|
+
}
|
|
49797
|
+
function scopedWorkerId(runId, value) {
|
|
49798
|
+
const slug = value.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48);
|
|
49799
|
+
return slug.startsWith(`${runId}-`) ? slug : `${runId}-${slug || "worker"}`;
|
|
49800
|
+
}
|
|
49801
|
+
function firstParagraph(value) {
|
|
49802
|
+
return value.split(/\n\s*\n/)[0]?.trim().slice(0, 800) || value.trim().slice(0, 800);
|
|
49803
|
+
}
|
|
49804
|
+
function inferVerificationState(value) {
|
|
49805
|
+
const lower = value.toLowerCase();
|
|
49806
|
+
if (lower.includes("partially verified") || lower.includes("partial")) return "partial";
|
|
49807
|
+
if (lower.includes("unverified") || lower.includes("not verified")) return "unverified";
|
|
49808
|
+
if (lower.includes("verified")) return "verified";
|
|
49809
|
+
return "pending";
|
|
49810
|
+
}
|
|
49811
|
+
function dependencyOutputContext(run, spec) {
|
|
49812
|
+
const dependencyIds = spec.dependsOn ?? [];
|
|
49813
|
+
if (dependencyIds.length === 0) return "";
|
|
49814
|
+
const blocks = dependencyIds.map((id) => run.workers.find((worker) => worker.id === id)).filter((worker) => Boolean(worker)).map((worker) => [
|
|
49815
|
+
`## Worker ${worker.id}`,
|
|
49816
|
+
`Role: ${worker.workerRole ?? "task"}`,
|
|
49817
|
+
`Goal: ${worker.goal}`,
|
|
49818
|
+
`Status: ${worker.status}`,
|
|
49819
|
+
worker.summary ? `Summary: ${worker.summary}` : "",
|
|
49820
|
+
worker.details ? `Details:
|
|
49821
|
+
${worker.details}` : ""
|
|
49822
|
+
].filter(Boolean).join("\n"));
|
|
49823
|
+
return blocks.length ? `# Prior worker outputs
|
|
49824
|
+
${blocks.join("\n\n")}` : "";
|
|
49825
|
+
}
|
|
49826
|
+
var terminal, WorkflowManager;
|
|
49827
|
+
var init_manager = __esm({
|
|
49828
|
+
"src/workflows/manager.ts"() {
|
|
49829
|
+
"use strict";
|
|
49830
|
+
init_agent_loop();
|
|
49831
|
+
init_providers();
|
|
49832
|
+
init_planner();
|
|
49833
|
+
init_persistence();
|
|
49834
|
+
init_readOnlyTools();
|
|
49835
|
+
terminal = /* @__PURE__ */ new Set(["cancelled", "completed", "failed"]);
|
|
49836
|
+
WorkflowManager = class {
|
|
49837
|
+
constructor(config, toolRuntime, systemPromptFn, bus, options = {}) {
|
|
49838
|
+
this.config = config;
|
|
49839
|
+
this.toolRuntime = toolRuntime;
|
|
49840
|
+
this.systemPromptFn = systemPromptFn;
|
|
49841
|
+
this.bus = bus;
|
|
49842
|
+
this.planner = options.planner;
|
|
49843
|
+
this.runner = options.runner ?? this.defaultRunner;
|
|
49844
|
+
}
|
|
49845
|
+
config;
|
|
49846
|
+
toolRuntime;
|
|
49847
|
+
systemPromptFn;
|
|
49848
|
+
bus;
|
|
49849
|
+
static {
|
|
49850
|
+
__name(this, "WorkflowManager");
|
|
49851
|
+
}
|
|
49852
|
+
runs = /* @__PURE__ */ new Map();
|
|
49853
|
+
planner;
|
|
49854
|
+
runner;
|
|
49855
|
+
async start(goal, sessionId) {
|
|
49856
|
+
const limits = resolveDynamicWorkflowConfig(this.config);
|
|
49857
|
+
if (!limits.enabled) {
|
|
49858
|
+
throw new Error("dynamic workflows are disabled in config");
|
|
49859
|
+
}
|
|
49860
|
+
const id = `wf-${randomUUID13().slice(0, 8)}`;
|
|
49861
|
+
const plan = await this.buildPlan(goal, id, limits);
|
|
49862
|
+
const now2 = Date.now();
|
|
49863
|
+
const run = {
|
|
49864
|
+
abortController: new AbortController(),
|
|
49865
|
+
activeAborts: /* @__PURE__ */ new Map(),
|
|
49866
|
+
concurrency: plan.concurrency,
|
|
49867
|
+
config: limits,
|
|
49868
|
+
goal,
|
|
49869
|
+
id,
|
|
49870
|
+
plannedWorkerCount: plan.specs.length,
|
|
49871
|
+
startedAt: now2,
|
|
49872
|
+
status: "running",
|
|
49873
|
+
workers: plan.specs.map((spec) => this.workerFromSpec(spec, id, plan.specs.length, now2))
|
|
49874
|
+
};
|
|
49875
|
+
this.runs.set(id, run);
|
|
49876
|
+
this.emitWorkflow("workflow.start", run, sessionId);
|
|
49877
|
+
for (const worker of run.workers) {
|
|
49878
|
+
this.emitSubagent("subagent.spawn_requested", run, worker, sessionId);
|
|
49879
|
+
}
|
|
49880
|
+
void this.executeRun(run, plan.specs, sessionId);
|
|
49881
|
+
return { run: this.snapshot(run), started: true };
|
|
49882
|
+
}
|
|
49883
|
+
status(id) {
|
|
49884
|
+
const run = id ? this.runs.get(id) : this.latestRun();
|
|
49885
|
+
if (run) return this.snapshot(run);
|
|
49886
|
+
if (id) return loadWorkflowSnapshot(id);
|
|
49887
|
+
const first = listWorkflowSnapshots(1)[0];
|
|
49888
|
+
return first ? loadWorkflowSnapshot(first.id) : null;
|
|
49889
|
+
}
|
|
49890
|
+
list(limit = 30) {
|
|
49891
|
+
const live = [...this.runs.values()].map((run) => ({
|
|
49892
|
+
finishedAt: run.finishedAt,
|
|
49893
|
+
goal: run.goal,
|
|
49894
|
+
id: run.id,
|
|
49895
|
+
plannedWorkerCount: run.plannedWorkerCount,
|
|
49896
|
+
startedAt: run.startedAt,
|
|
49897
|
+
status: run.status,
|
|
49898
|
+
workerCount: run.workers.length
|
|
49899
|
+
}));
|
|
49900
|
+
const persisted = listWorkflowSnapshots(limit);
|
|
49901
|
+
const seen = new Set(live.map((entry) => entry.id));
|
|
49902
|
+
return [...live, ...persisted.filter((entry) => !seen.has(entry.id))].sort((a, b) => (b.finishedAt ?? b.startedAt) - (a.finishedAt ?? a.startedAt)).slice(0, Math.max(1, limit));
|
|
49903
|
+
}
|
|
49904
|
+
cancel(id) {
|
|
49905
|
+
const run = this.runs.get(id);
|
|
49906
|
+
if (run) {
|
|
49907
|
+
this.cancelRun(run);
|
|
49908
|
+
return { found: true, run: this.snapshot(run) };
|
|
49909
|
+
}
|
|
49910
|
+
for (const candidate of this.runs.values()) {
|
|
49911
|
+
const worker = candidate.workers.find((item) => item.id === id);
|
|
49912
|
+
if (!worker) continue;
|
|
49913
|
+
candidate.activeAborts.get(worker.id)?.();
|
|
49914
|
+
this.updateWorker(candidate, worker.id, {
|
|
49915
|
+
currentStep: "cancel requested",
|
|
49916
|
+
status: "interrupted"
|
|
49917
|
+
});
|
|
49918
|
+
return { found: true, run: this.snapshot(candidate), workerId: worker.id };
|
|
49919
|
+
}
|
|
49920
|
+
return { found: false };
|
|
49921
|
+
}
|
|
49922
|
+
async buildPlan(goal, runId, limits) {
|
|
49923
|
+
if (this.planner) {
|
|
49924
|
+
return ensureSynthesizer(await this.planner(goal, runId, limits), goal, runId, limits);
|
|
49925
|
+
}
|
|
49926
|
+
try {
|
|
49927
|
+
const provider = createProvider(this.config);
|
|
49928
|
+
const controller = new AbortController();
|
|
49929
|
+
const timer = setTimeout(() => controller.abort(), Math.min(6e4, limits.workerTimeoutMs));
|
|
49930
|
+
try {
|
|
49931
|
+
const result = await provider.chat({
|
|
49932
|
+
systemPrompt: buildPlannerSystemPrompt(limits),
|
|
49933
|
+
messages: [{ role: "user", content: goal }],
|
|
49934
|
+
tools: [],
|
|
49935
|
+
signal: controller.signal
|
|
49936
|
+
});
|
|
49937
|
+
const parsed = parseModelPlan(result.text ?? "", goal, runId, limits);
|
|
49938
|
+
if (parsed.specs.length > 0) return parsed;
|
|
49939
|
+
} finally {
|
|
49940
|
+
clearTimeout(timer);
|
|
49941
|
+
}
|
|
49942
|
+
} catch {
|
|
49943
|
+
}
|
|
49944
|
+
return ensureSynthesizer(planWorkflow(goal, runId, limits), goal, runId, limits);
|
|
49945
|
+
}
|
|
49946
|
+
async executeRun(run, specs, sessionId) {
|
|
49947
|
+
const queue = [...specs];
|
|
49948
|
+
const active = /* @__PURE__ */ new Map();
|
|
49949
|
+
let stableCompletions = 0;
|
|
49950
|
+
try {
|
|
49951
|
+
while ((queue.length > 0 || active.size > 0) && !run.abortController.signal.aborted) {
|
|
49952
|
+
const ready = queue.filter((spec) => this.dependenciesFinished(run, spec));
|
|
49953
|
+
while (ready.length > 0 && active.size < run.concurrency && !run.abortController.signal.aborted) {
|
|
49954
|
+
const spec = ready.shift();
|
|
49955
|
+
queue.splice(queue.indexOf(spec), 1);
|
|
49956
|
+
const promise = this.executeWorker(run, spec, sessionId).then(() => {
|
|
49957
|
+
stableCompletions += 1;
|
|
49958
|
+
if (stableCompletions % Math.max(2, run.concurrency) === 0) {
|
|
49959
|
+
run.concurrency = adjustConcurrency(run.concurrency, run.plannedWorkerCount, run.config, "stable_completion");
|
|
49960
|
+
}
|
|
49961
|
+
}).catch((err) => {
|
|
49962
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
49963
|
+
if (/rate|429|quota|throttle/i.test(message)) {
|
|
49964
|
+
run.concurrency = adjustConcurrency(run.concurrency, run.plannedWorkerCount, run.config, "rate_limited");
|
|
49965
|
+
}
|
|
49966
|
+
}).finally(() => active.delete(spec.id));
|
|
49967
|
+
active.set(spec.id, promise);
|
|
49968
|
+
}
|
|
49969
|
+
if (active.size === 0) break;
|
|
49970
|
+
await Promise.race(active.values());
|
|
49971
|
+
}
|
|
49972
|
+
if (run.abortController.signal.aborted) {
|
|
49973
|
+
for (const worker of run.workers) {
|
|
49974
|
+
if (worker.status === "queued" || worker.status === "running") {
|
|
49975
|
+
this.updateWorker(run, worker.id, { currentStep: "cancelled", status: "interrupted" });
|
|
49976
|
+
}
|
|
49977
|
+
}
|
|
49978
|
+
run.status = "cancelled";
|
|
49979
|
+
run.summary = this.summarize(run, "cancelled");
|
|
49980
|
+
} else if (run.workers.some((worker) => worker.status === "failed")) {
|
|
49981
|
+
run.status = "failed";
|
|
49982
|
+
run.summary = this.summarize(run, "failed");
|
|
49983
|
+
} else {
|
|
49984
|
+
run.status = "completed";
|
|
49985
|
+
run.summary = this.summarize(run, "completed");
|
|
49986
|
+
}
|
|
49987
|
+
} catch (err) {
|
|
49988
|
+
run.status = "failed";
|
|
49989
|
+
run.summary = `Workflow failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
49990
|
+
} finally {
|
|
49991
|
+
run.finishedAt = Date.now();
|
|
49992
|
+
if (run.config.persistHistory) saveWorkflowSnapshot(this.snapshot(run));
|
|
49993
|
+
this.emitWorkflow(run.status === "failed" ? "workflow.error" : "workflow.complete", run, sessionId);
|
|
49994
|
+
}
|
|
49995
|
+
}
|
|
49996
|
+
async executeWorker(run, spec, sessionId) {
|
|
49997
|
+
if (run.abortController.signal.aborted) return;
|
|
49998
|
+
this.updateWorker(run, spec.id, {
|
|
49999
|
+
currentStep: "starting",
|
|
50000
|
+
startedAt: Date.now(),
|
|
50001
|
+
status: "running"
|
|
50002
|
+
});
|
|
50003
|
+
this.emitSubagent("subagent.start", run, this.worker(run, spec.id), sessionId);
|
|
50004
|
+
const controller = new AbortController();
|
|
50005
|
+
const timeoutMs = spec.role === "synthesizer" ? run.config.workerTimeoutMs * 3 : run.config.workerTimeoutMs;
|
|
50006
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
50007
|
+
run.activeAborts.set(spec.id, () => controller.abort());
|
|
50008
|
+
const signal = AbortSignal.any([controller.signal, run.abortController.signal]);
|
|
50009
|
+
try {
|
|
50010
|
+
const result = await this.runner({
|
|
50011
|
+
config: this.config,
|
|
50012
|
+
run: this.snapshot(run),
|
|
50013
|
+
signal,
|
|
50014
|
+
spec,
|
|
50015
|
+
systemPromptFn: this.systemPromptFn,
|
|
50016
|
+
toolRuntime: new ReadOnlyToolRuntime(this.toolRuntime),
|
|
50017
|
+
update: /* @__PURE__ */ __name((patch) => {
|
|
50018
|
+
this.updateWorker(run, spec.id, patch);
|
|
50019
|
+
this.emitSubagent("subagent.progress", run, this.worker(run, spec.id), sessionId);
|
|
50020
|
+
}, "update")
|
|
50021
|
+
});
|
|
50022
|
+
this.updateWorker(run, spec.id, {
|
|
50023
|
+
currentStep: "complete",
|
|
50024
|
+
details: result,
|
|
50025
|
+
durationSeconds: (Date.now() - (this.worker(run, spec.id).startedAt ?? Date.now())) / 1e3,
|
|
50026
|
+
status: signal.aborted ? "interrupted" : "completed",
|
|
50027
|
+
summary: firstParagraph(result),
|
|
50028
|
+
verificationState: spec.role === "verifier" ? inferVerificationState(result) : "not_applicable"
|
|
50029
|
+
});
|
|
50030
|
+
} catch (err) {
|
|
50031
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
50032
|
+
this.updateWorker(run, spec.id, {
|
|
50033
|
+
currentStep: signal.aborted ? "interrupted" : "failed",
|
|
50034
|
+
details: message,
|
|
50035
|
+
durationSeconds: (Date.now() - (this.worker(run, spec.id).startedAt ?? Date.now())) / 1e3,
|
|
50036
|
+
status: signal.aborted ? "interrupted" : "failed",
|
|
50037
|
+
summary: message,
|
|
50038
|
+
verificationState: "unverified"
|
|
50039
|
+
});
|
|
50040
|
+
if (!signal.aborted) throw err;
|
|
50041
|
+
} finally {
|
|
50042
|
+
clearTimeout(timeout);
|
|
50043
|
+
run.activeAborts.delete(spec.id);
|
|
50044
|
+
this.emitSubagent("subagent.complete", run, this.worker(run, spec.id), sessionId);
|
|
50045
|
+
if (run.config.persistHistory) saveWorkflowSnapshot(this.snapshot(run));
|
|
50046
|
+
}
|
|
50047
|
+
}
|
|
50048
|
+
defaultRunner = /* @__PURE__ */ __name(async (ctx) => {
|
|
50049
|
+
const workerConfig = {
|
|
50050
|
+
...ctx.config,
|
|
50051
|
+
features: { ...ctx.config.features, skill_auto_suggest: false }
|
|
50052
|
+
};
|
|
50053
|
+
const loop = new AgentLoop(workerConfig, ctx.toolRuntime);
|
|
50054
|
+
const sections = await ctx.systemPromptFn();
|
|
50055
|
+
const systemPrompt = [
|
|
50056
|
+
...sections,
|
|
50057
|
+
"# Advisory Workflow Worker Rules",
|
|
50058
|
+
"You are a read-only workflow worker. Never modify files, execute shell/code, send messages, update memory, or perform browser actions that change state.",
|
|
50059
|
+
"Use available read/search tools only. Return concise evidence-backed findings."
|
|
50060
|
+
].filter(Boolean).join("\n\n");
|
|
50061
|
+
ctx.signal.addEventListener("abort", () => loop.abort(), { once: true });
|
|
50062
|
+
let answer = "";
|
|
50063
|
+
let streamedText = "";
|
|
50064
|
+
let outputTail = [];
|
|
50065
|
+
let toolCount = 0;
|
|
50066
|
+
const userPrompt = [ctx.spec.prompt, dependencyOutputContext(ctx.run, ctx.spec)].filter(Boolean).join("\n\n");
|
|
50067
|
+
for await (const chunk of loop.run(userPrompt, systemPrompt)) {
|
|
50068
|
+
if (ctx.signal.aborted) {
|
|
50069
|
+
loop.abort();
|
|
50070
|
+
break;
|
|
50071
|
+
}
|
|
50072
|
+
if (chunk.type === "thinking" && chunk.content.trim()) {
|
|
50073
|
+
streamedText += chunk.content;
|
|
50074
|
+
ctx.update({ currentStep: chunk.content.trim().slice(0, 180), thinking: [chunk.content.trim().slice(0, 500)] });
|
|
50075
|
+
}
|
|
50076
|
+
if (chunk.type === "tool_call") {
|
|
50077
|
+
toolCount += 1;
|
|
50078
|
+
ctx.update({ currentStep: `using ${chunk.toolName ?? "tool"}`, toolCount, tools: [`${chunk.toolName ?? "tool"}(...)`] });
|
|
50079
|
+
}
|
|
50080
|
+
if (chunk.type === "tool_result") {
|
|
50081
|
+
outputTail = [...outputTail, { isError: /error/i.test(chunk.content), preview: chunk.content.slice(0, 240), tool: chunk.toolName ?? "tool" }].slice(-8);
|
|
50082
|
+
ctx.update({ outputTail });
|
|
50083
|
+
}
|
|
50084
|
+
if (chunk.type === "answer" && chunk.content.trim()) answer = chunk.content.trim();
|
|
50085
|
+
}
|
|
50086
|
+
const result = answer || streamedText.trim();
|
|
50087
|
+
if (ctx.signal.aborted) {
|
|
50088
|
+
if (result) {
|
|
50089
|
+
return `${result}
|
|
50090
|
+
|
|
50091
|
+
[Partial output preserved after worker interruption.]`;
|
|
50092
|
+
}
|
|
50093
|
+
throw new Error("worker interrupted");
|
|
50094
|
+
}
|
|
50095
|
+
return result || "Worker completed with no final answer.";
|
|
50096
|
+
}, "defaultRunner");
|
|
50097
|
+
workerFromSpec(spec, workflowId, taskCount, now2) {
|
|
50098
|
+
return {
|
|
50099
|
+
currentStep: "queued",
|
|
50100
|
+
depth: spec.depth,
|
|
50101
|
+
goal: spec.goal,
|
|
50102
|
+
id: spec.id,
|
|
50103
|
+
index: spec.index,
|
|
50104
|
+
notes: [],
|
|
50105
|
+
parentId: spec.parentId,
|
|
50106
|
+
startedAt: now2,
|
|
50107
|
+
status: "queued",
|
|
50108
|
+
taskCount,
|
|
50109
|
+
thinking: [],
|
|
50110
|
+
toolCount: 0,
|
|
50111
|
+
tools: [],
|
|
50112
|
+
verificationState: spec.role === "verifier" ? "pending" : "not_applicable",
|
|
50113
|
+
workflowId,
|
|
50114
|
+
workerRole: spec.role
|
|
50115
|
+
};
|
|
50116
|
+
}
|
|
50117
|
+
dependenciesFinished(run, spec) {
|
|
50118
|
+
return (spec.dependsOn ?? []).every((id) => {
|
|
50119
|
+
const status = run.workers.find((worker) => worker.id === id)?.status;
|
|
50120
|
+
return status === "completed" || status === "failed" || status === "interrupted";
|
|
50121
|
+
});
|
|
50122
|
+
}
|
|
50123
|
+
updateWorker(run, id, patch) {
|
|
50124
|
+
run.workers = run.workers.map((worker) => worker.id === id ? { ...worker, ...patch } : worker);
|
|
50125
|
+
}
|
|
50126
|
+
worker(run, id) {
|
|
50127
|
+
return run.workers.find((worker) => worker.id === id) ?? run.workers[0];
|
|
50128
|
+
}
|
|
50129
|
+
snapshot(run) {
|
|
50130
|
+
return {
|
|
50131
|
+
concurrency: run.concurrency,
|
|
50132
|
+
finishedAt: run.finishedAt,
|
|
50133
|
+
goal: run.goal,
|
|
50134
|
+
id: run.id,
|
|
50135
|
+
plannedWorkerCount: run.plannedWorkerCount,
|
|
50136
|
+
startedAt: run.startedAt,
|
|
50137
|
+
status: run.status,
|
|
50138
|
+
summary: run.summary,
|
|
50139
|
+
workers: run.workers.map((worker) => ({ ...worker }))
|
|
50140
|
+
};
|
|
50141
|
+
}
|
|
50142
|
+
latestRun() {
|
|
50143
|
+
const runs = [...this.runs.values()].sort((a, b) => b.startedAt - a.startedAt);
|
|
50144
|
+
return runs[0] ?? null;
|
|
50145
|
+
}
|
|
50146
|
+
cancelRun(run) {
|
|
50147
|
+
if (terminal.has(run.status)) return;
|
|
50148
|
+
run.abortController.abort();
|
|
50149
|
+
for (const abort of run.activeAborts.values()) abort();
|
|
50150
|
+
run.status = "cancelled";
|
|
50151
|
+
}
|
|
50152
|
+
summarize(run, status) {
|
|
50153
|
+
const synthesizer = run.workers.find((worker) => worker.workerRole === "synthesizer" && worker.details && worker.details !== "worker interrupted");
|
|
50154
|
+
if ((status === "completed" || status === "cancelled" || status === "failed") && synthesizer?.details) {
|
|
50155
|
+
return [synthesizer.details, "No files were modified by advisory workflow workers."].join("\n\n");
|
|
50156
|
+
}
|
|
50157
|
+
const counts = run.workers.reduce((acc, worker) => {
|
|
50158
|
+
acc[worker.status] = (acc[worker.status] ?? 0) + 1;
|
|
50159
|
+
return acc;
|
|
50160
|
+
}, {});
|
|
50161
|
+
const highlights = run.workers.filter((worker) => worker.summary).slice(0, 8).map((worker) => `- ${worker.goal}: ${worker.summary}`).join("\n");
|
|
50162
|
+
return [
|
|
50163
|
+
`Workflow ${status}: ${run.goal}`,
|
|
50164
|
+
`Workers: ${run.workers.length} planned \xB7 ${counts.completed ?? 0} completed \xB7 ${counts.failed ?? 0} failed \xB7 ${counts.interrupted ?? 0} interrupted.`,
|
|
50165
|
+
"No files were modified by advisory workflow workers.",
|
|
50166
|
+
highlights ? `
|
|
50167
|
+
Key worker summaries:
|
|
50168
|
+
${highlights}` : ""
|
|
50169
|
+
].filter(Boolean).join("\n");
|
|
50170
|
+
}
|
|
50171
|
+
emitWorkflow(type, run, sessionId) {
|
|
50172
|
+
this.bus.emitEvent({
|
|
50173
|
+
payload: this.snapshot(run),
|
|
50174
|
+
session_id: sessionId ?? void 0,
|
|
50175
|
+
type
|
|
50176
|
+
});
|
|
50177
|
+
}
|
|
50178
|
+
emitSubagent(type, run, worker, sessionId) {
|
|
50179
|
+
this.bus.emitEvent({
|
|
50180
|
+
payload: {
|
|
50181
|
+
cost_usd: worker.costUsd,
|
|
50182
|
+
current_step: worker.currentStep,
|
|
50183
|
+
depth: worker.depth,
|
|
50184
|
+
details: worker.details,
|
|
50185
|
+
duration_seconds: worker.durationSeconds,
|
|
50186
|
+
files_read: worker.filesRead,
|
|
50187
|
+
files_written: worker.filesWritten,
|
|
50188
|
+
goal: worker.goal,
|
|
50189
|
+
input_tokens: worker.inputTokens,
|
|
50190
|
+
output_tail: worker.outputTail?.map((entry) => ({ is_error: entry.isError, preview: entry.preview, tool: entry.tool })),
|
|
50191
|
+
output_tokens: worker.outputTokens,
|
|
50192
|
+
parent_id: worker.parentId,
|
|
50193
|
+
status: worker.status,
|
|
50194
|
+
subagent_id: worker.id,
|
|
50195
|
+
summary: worker.summary,
|
|
50196
|
+
task_count: worker.taskCount,
|
|
50197
|
+
task_index: worker.index,
|
|
50198
|
+
text: worker.currentStep ?? worker.summary,
|
|
50199
|
+
tool_count: worker.toolCount,
|
|
50200
|
+
verification_state: worker.verificationState,
|
|
50201
|
+
workflow_id: run.id,
|
|
50202
|
+
worker_role: worker.workerRole
|
|
50203
|
+
},
|
|
50204
|
+
session_id: sessionId ?? void 0,
|
|
50205
|
+
type
|
|
50206
|
+
});
|
|
50207
|
+
}
|
|
50208
|
+
};
|
|
50209
|
+
__name(buildPlannerSystemPrompt, "buildPlannerSystemPrompt");
|
|
50210
|
+
__name(parseModelPlan, "parseModelPlan");
|
|
50211
|
+
__name(ensureSynthesizer, "ensureSynthesizer");
|
|
50212
|
+
__name(buildModelWorkerPrompt, "buildModelWorkerPrompt");
|
|
50213
|
+
__name(normalizeRole, "normalizeRole");
|
|
50214
|
+
__name(extractJsonObject, "extractJsonObject");
|
|
50215
|
+
__name(uniqueWorkerId, "uniqueWorkerId");
|
|
50216
|
+
__name(scopedWorkerId, "scopedWorkerId");
|
|
50217
|
+
__name(firstParagraph, "firstParagraph");
|
|
50218
|
+
__name(inferVerificationState, "inferVerificationState");
|
|
50219
|
+
__name(dependencyOutputContext, "dependencyOutputContext");
|
|
50220
|
+
}
|
|
50221
|
+
});
|
|
50222
|
+
|
|
50223
|
+
// src/rpcHandlers.ts
|
|
50224
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
50225
|
+
import { randomUUID as randomUUID14 } from "node:crypto";
|
|
50226
|
+
import { existsSync as existsSync32, mkdirSync as mkdirSync18, readFileSync as readFileSync29, rmSync, statSync as statSync4, writeFileSync as writeFileSync20 } from "node:fs";
|
|
50227
|
+
import { homedir as homedir29 } from "node:os";
|
|
50228
|
+
import { basename as basename4, extname as extname4, join as join43 } from "node:path";
|
|
48965
50229
|
function registerRpcHandlers(options) {
|
|
48966
50230
|
const { agentConfig, agentLoop, bridgeEmitter, bridgeManager, bus, mcpManager, systemPromptFn, toolRegistry, voiceManager } = options;
|
|
48967
50231
|
let currentRun = null;
|
|
48968
50232
|
const pendingResponders = /* @__PURE__ */ new Map();
|
|
48969
50233
|
const promptCollector = createPromptCollector(bus, () => agentLoop.sessionId);
|
|
50234
|
+
const workflowManager = new WorkflowManager(agentConfig, toolRegistry, systemPromptFn, bus);
|
|
48970
50235
|
const OAUTH_FLOW_TTL_MS = 15 * 60 * 1e3;
|
|
48971
50236
|
const oauthFlows = /* @__PURE__ */ new Map();
|
|
48972
50237
|
const cleanupOAuthFlow = /* @__PURE__ */ __name((flowId, abort) => {
|
|
@@ -48985,7 +50250,7 @@ function registerRpcHandlers(options) {
|
|
|
48985
50250
|
const user = bridgeUser(rawEvent, source);
|
|
48986
50251
|
const text = formatBridgeText(rawEvent, source, user);
|
|
48987
50252
|
bus.emitEvent({
|
|
48988
|
-
payload: { source, task_id:
|
|
50253
|
+
payload: { source, task_id: randomUUID14(), text, user },
|
|
48989
50254
|
session_id: agentLoop.sessionId,
|
|
48990
50255
|
type: "bridge.message"
|
|
48991
50256
|
});
|
|
@@ -48994,7 +50259,7 @@ function registerRpcHandlers(options) {
|
|
|
48994
50259
|
bus.registerRpc("setup.status", () => {
|
|
48995
50260
|
const hasConfigKey = Boolean(agentConfig.llm.api_key && agentConfig.llm.api_key !== "proxy-token");
|
|
48996
50261
|
const hasEnvKey = Boolean(
|
|
48997
|
-
process.env.ANTHROPIC_AUTH_TOKEN || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.GITHUB_TOKEN
|
|
50262
|
+
process.env.ANTHROPIC_AUTH_TOKEN || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || process.env.GITHUB_COPILOT_TOKEN || process.env.GITHUB_TOKEN
|
|
48998
50263
|
);
|
|
48999
50264
|
const hasStoredCredential = PROVIDERS2.some((p) => {
|
|
49000
50265
|
try {
|
|
@@ -49342,12 +50607,12 @@ ${helpMessage}` : field.label;
|
|
|
49342
50607
|
const id = String(params.session_id ?? agentLoop.sessionId) || agentLoop.sessionId;
|
|
49343
50608
|
const data = loadSession(id);
|
|
49344
50609
|
const messages = data?.messages ?? agentLoop.history;
|
|
49345
|
-
const exportDir =
|
|
49346
|
-
if (!
|
|
49347
|
-
|
|
50610
|
+
const exportDir = join43(homedir29(), ".openjaw-agent", "exports");
|
|
50611
|
+
if (!existsSync32(exportDir)) {
|
|
50612
|
+
mkdirSync18(exportDir, { recursive: true });
|
|
49348
50613
|
}
|
|
49349
50614
|
const safeId = id.replace(/[^a-zA-Z0-9_.-]/g, "_") || agentLoop.sessionId;
|
|
49350
|
-
const file2 =
|
|
50615
|
+
const file2 = join43(exportDir, `session-${safeId}.md`);
|
|
49351
50616
|
const lines = [
|
|
49352
50617
|
`# OpenJaw Agent Session ${id}`,
|
|
49353
50618
|
`Date: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
@@ -49360,7 +50625,7 @@ ${helpMessage}` : field.label;
|
|
|
49360
50625
|
for (const message of messages) {
|
|
49361
50626
|
lines.push(...sessionMessageToMarkdown(message));
|
|
49362
50627
|
}
|
|
49363
|
-
|
|
50628
|
+
writeFileSync20(file2, lines.join("\n"), "utf-8");
|
|
49364
50629
|
return { file: file2, message_count: messages.length };
|
|
49365
50630
|
});
|
|
49366
50631
|
bus.registerRpc("session.interrupt", () => {
|
|
@@ -49377,8 +50642,8 @@ ${helpMessage}` : field.label;
|
|
|
49377
50642
|
return { deleted: "" };
|
|
49378
50643
|
}
|
|
49379
50644
|
try {
|
|
49380
|
-
const file2 =
|
|
49381
|
-
if (
|
|
50645
|
+
const file2 = join43(homedir29(), ".openjaw-agent", "sessions", `${id}.json`);
|
|
50646
|
+
if (existsSync32(file2)) {
|
|
49382
50647
|
rmSync(file2);
|
|
49383
50648
|
}
|
|
49384
50649
|
return { deleted: id };
|
|
@@ -49451,11 +50716,11 @@ ${helpMessage}` : field.label;
|
|
|
49451
50716
|
bus.registerRpc("shell.exec", async (params) => {
|
|
49452
50717
|
const command = String(params.command ?? "");
|
|
49453
50718
|
if (!command) return { code: -1, stderr: "empty command" };
|
|
49454
|
-
return await new Promise((
|
|
50719
|
+
return await new Promise((resolve6) => {
|
|
49455
50720
|
const isWin = process.platform === "win32";
|
|
49456
50721
|
const shell = isWin ? "powershell.exe" : "sh";
|
|
49457
50722
|
const args = isWin ? ["-NoProfile", "-Command", command] : ["-c", command];
|
|
49458
|
-
const child =
|
|
50723
|
+
const child = spawn6(shell, args, { timeout: 3e4 });
|
|
49459
50724
|
let stdout = "";
|
|
49460
50725
|
let stderr = "";
|
|
49461
50726
|
let truncated = false;
|
|
@@ -49478,10 +50743,10 @@ ${helpMessage}` : field.label;
|
|
|
49478
50743
|
if (truncated) {
|
|
49479
50744
|
stderr += "\n[output truncated to 1MB]";
|
|
49480
50745
|
}
|
|
49481
|
-
|
|
50746
|
+
resolve6({ code: code ?? -1, stderr, stdout });
|
|
49482
50747
|
});
|
|
49483
50748
|
child.on("error", (err) => {
|
|
49484
|
-
|
|
50749
|
+
resolve6({ code: -1, stderr: err.message });
|
|
49485
50750
|
});
|
|
49486
50751
|
});
|
|
49487
50752
|
});
|
|
@@ -49603,7 +50868,7 @@ ${helpMessage}` : field.label;
|
|
|
49603
50868
|
return agentConfig.llm.copilot_enterprise_url;
|
|
49604
50869
|
})();
|
|
49605
50870
|
const flow = await startCopilotDeviceFlow(clientId, enterpriseUrl);
|
|
49606
|
-
const flowId =
|
|
50871
|
+
const flowId = randomUUID14();
|
|
49607
50872
|
const controller = new AbortController();
|
|
49608
50873
|
const timer = setTimeout(() => {
|
|
49609
50874
|
cleanupOAuthFlow(flowId, true);
|
|
@@ -49767,7 +51032,7 @@ ${helpMessage}` : field.label;
|
|
|
49767
51032
|
Object.assign(agentConfig.llm, result.configUpdates);
|
|
49768
51033
|
bus.emitEvent({ payload: sessionInfoSnapshot(agentLoop, toolRegistry), session_id: agentLoop.sessionId, type: "session.info" });
|
|
49769
51034
|
}
|
|
49770
|
-
return { messages };
|
|
51035
|
+
return { config_updates: result.configUpdates, messages };
|
|
49771
51036
|
});
|
|
49772
51037
|
bus.registerRpc("provider.disconnect", (params) => {
|
|
49773
51038
|
const provider = String(params.provider ?? "").trim();
|
|
@@ -49827,6 +51092,32 @@ ${helpMessage}` : field.label;
|
|
|
49827
51092
|
tasks: forks.map((task) => ({ id: task.id, prompt: task.prompt, status: task.status }))
|
|
49828
51093
|
};
|
|
49829
51094
|
});
|
|
51095
|
+
bus.registerRpc("workflow.start", async (params) => {
|
|
51096
|
+
const goal = String(params.goal ?? params.prompt ?? "").trim();
|
|
51097
|
+
if (!goal) return { error: "usage: /workflow <goal>", ok: false };
|
|
51098
|
+
const result = await workflowManager.start(goal, agentLoop.sessionId);
|
|
51099
|
+
return { ok: true, run: result.run };
|
|
51100
|
+
});
|
|
51101
|
+
bus.registerRpc("workflow.status", (params) => {
|
|
51102
|
+
const id = String(params.id ?? params.run_id ?? "").trim();
|
|
51103
|
+
const run = workflowManager.status(id || void 0);
|
|
51104
|
+
return run ? { ok: true, run } : { error: id ? `workflow not found: ${id}` : "no workflows yet", ok: false };
|
|
51105
|
+
});
|
|
51106
|
+
bus.registerRpc("workflow.list", (params) => {
|
|
51107
|
+
const limit = Number(params.limit ?? 30);
|
|
51108
|
+
return { ok: true, runs: workflowManager.list(Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 30) };
|
|
51109
|
+
});
|
|
51110
|
+
bus.registerRpc("workflow.show", (params) => {
|
|
51111
|
+
const id = String(params.id ?? params.run_id ?? "").trim();
|
|
51112
|
+
const run = workflowManager.status(id || void 0);
|
|
51113
|
+
return run ? { ok: true, run, summary: run.summary ?? "" } : { error: id ? `workflow not found: ${id}` : "no workflows yet", ok: false };
|
|
51114
|
+
});
|
|
51115
|
+
bus.registerRpc("workflow.cancel", (params) => {
|
|
51116
|
+
const id = String(params.id ?? params.run_id ?? params.worker_id ?? "").trim();
|
|
51117
|
+
if (!id) return { error: "usage: /workflow cancel <runId|workerId>", ok: false };
|
|
51118
|
+
const result = workflowManager.cancel(id);
|
|
51119
|
+
return result.found ? { ok: true, ...result } : { error: `workflow or worker not found: ${id}`, ok: false };
|
|
51120
|
+
});
|
|
49830
51121
|
bus.registerRpc("schedule.add", (params) => {
|
|
49831
51122
|
const prompt = String(params.prompt ?? "").trim();
|
|
49832
51123
|
const raw = String(params.schedule ?? "").trim();
|
|
@@ -50097,13 +51388,13 @@ ${helpMessage}` : field.label;
|
|
|
50097
51388
|
const firstSpace = raw.search(/\s/);
|
|
50098
51389
|
const path3 = firstSpace > 0 ? raw.slice(0, firstSpace) : raw;
|
|
50099
51390
|
const remainder = firstSpace > 0 ? raw.slice(firstSpace).trim() : "";
|
|
50100
|
-
if (!
|
|
51391
|
+
if (!existsSync32(path3)) {
|
|
50101
51392
|
throw new Error(`image.attach: file not found: ${path3}`);
|
|
50102
51393
|
}
|
|
50103
51394
|
let buffer;
|
|
50104
51395
|
let fileSize = 0;
|
|
50105
51396
|
try {
|
|
50106
|
-
buffer =
|
|
51397
|
+
buffer = readFileSync29(path3);
|
|
50107
51398
|
fileSize = statSync4(path3).size;
|
|
50108
51399
|
} catch (err) {
|
|
50109
51400
|
throw new Error(`image.attach: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -50113,12 +51404,12 @@ ${helpMessage}` : field.label;
|
|
|
50113
51404
|
pendingImage = {
|
|
50114
51405
|
base64: buffer.toString("base64"),
|
|
50115
51406
|
mimeType,
|
|
50116
|
-
name:
|
|
51407
|
+
name: basename4(path3)
|
|
50117
51408
|
};
|
|
50118
51409
|
const tokenEstimate = Math.max(1, Math.ceil(buffer.byteLength / 750));
|
|
50119
51410
|
return {
|
|
50120
51411
|
height: 0,
|
|
50121
|
-
name:
|
|
51412
|
+
name: basename4(path3),
|
|
50122
51413
|
remainder,
|
|
50123
51414
|
token_estimate: tokenEstimate,
|
|
50124
51415
|
width: 0
|
|
@@ -50149,13 +51440,13 @@ ${helpMessage}` : field.label;
|
|
|
50149
51440
|
}
|
|
50150
51441
|
});
|
|
50151
51442
|
bus.registerRpc("reload.env", () => {
|
|
50152
|
-
const envPath =
|
|
50153
|
-
if (!
|
|
51443
|
+
const envPath = join43(homedir29(), ".openjaw-agent", ".env");
|
|
51444
|
+
if (!existsSync32(envPath)) {
|
|
50154
51445
|
return { updated: 0 };
|
|
50155
51446
|
}
|
|
50156
51447
|
let updated = 0;
|
|
50157
51448
|
try {
|
|
50158
|
-
const raw =
|
|
51449
|
+
const raw = readFileSync29(envPath, "utf-8");
|
|
50159
51450
|
for (const line of raw.split(/\r?\n/)) {
|
|
50160
51451
|
const trimmed = line.trim();
|
|
50161
51452
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
@@ -50195,8 +51486,25 @@ ${helpMessage}` : field.label;
|
|
|
50195
51486
|
error: "checkpoints are not enabled in this build",
|
|
50196
51487
|
success: false
|
|
50197
51488
|
}));
|
|
50198
|
-
bus.registerRpc("spawn_tree.
|
|
50199
|
-
|
|
51489
|
+
bus.registerRpc("spawn_tree.save", (params) => ({
|
|
51490
|
+
path: saveSpawnTreeSnapshot({
|
|
51491
|
+
finished_at: typeof params.finished_at === "number" ? params.finished_at : Date.now() / 1e3,
|
|
51492
|
+
label: typeof params.label === "string" ? params.label : void 0,
|
|
51493
|
+
session_id: typeof params.session_id === "string" ? params.session_id : agentLoop.sessionId ?? "default",
|
|
51494
|
+
started_at: typeof params.started_at === "number" || params.started_at === null ? params.started_at : null,
|
|
51495
|
+
subagents: Array.isArray(params.subagents) ? params.subagents : []
|
|
51496
|
+
})
|
|
51497
|
+
}));
|
|
51498
|
+
bus.registerRpc("spawn_tree.list", (params) => ({
|
|
51499
|
+
entries: listSpawnTreeSnapshots(
|
|
51500
|
+
typeof params.session_id === "string" ? params.session_id : agentLoop.sessionId ?? "default",
|
|
51501
|
+
typeof params.limit === "number" ? params.limit : 30
|
|
51502
|
+
)
|
|
51503
|
+
}));
|
|
51504
|
+
bus.registerRpc("spawn_tree.load", (params) => {
|
|
51505
|
+
const path3 = String(params.path ?? "").trim();
|
|
51506
|
+
return path3 ? loadSpawnTreeSnapshot(path3) ?? { subagents: [] } : { subagents: [] };
|
|
51507
|
+
});
|
|
50200
51508
|
bus.registerRpc("skills.reload", async () => {
|
|
50201
51509
|
try {
|
|
50202
51510
|
clearSkillsCache();
|
|
@@ -50280,9 +51588,19 @@ ${helpMessage}` : field.label;
|
|
|
50280
51588
|
}
|
|
50281
51589
|
return { output: name ? `unknown command: /${name}` : "(no command)", type: "exec" };
|
|
50282
51590
|
});
|
|
50283
|
-
bus.registerRpc("delegation.status", () => ({
|
|
51591
|
+
bus.registerRpc("delegation.status", () => ({
|
|
51592
|
+
active: [],
|
|
51593
|
+
max_concurrent_children: agentConfig.features?.dynamic_workflows?.hard_max_concurrent_workers ?? 128,
|
|
51594
|
+
max_spawn_depth: agentConfig.features?.dynamic_workflows?.hard_max_workers ?? 1024,
|
|
51595
|
+
paused: false
|
|
51596
|
+
}));
|
|
50284
51597
|
bus.registerRpc("delegation.pause", (params) => ({ paused: Boolean(params.paused) }));
|
|
50285
|
-
bus.registerRpc("subagent.interrupt", () =>
|
|
51598
|
+
bus.registerRpc("subagent.interrupt", (params) => {
|
|
51599
|
+
const id = String(params.subagent_id ?? "").trim();
|
|
51600
|
+
if (!id) return { found: false };
|
|
51601
|
+
const result = workflowManager.cancel(id);
|
|
51602
|
+
return { found: result.found, subagent_id: id };
|
|
51603
|
+
});
|
|
50286
51604
|
syncMcpTools(mcpManager, toolRegistry);
|
|
50287
51605
|
bus.emitEvent({
|
|
50288
51606
|
payload: sessionInfoSnapshot(agentLoop, toolRegistry),
|
|
@@ -50330,6 +51648,8 @@ var init_rpcHandlers = __esm({
|
|
|
50330
51648
|
init_registry2();
|
|
50331
51649
|
init_usage();
|
|
50332
51650
|
init_usageSnapshot();
|
|
51651
|
+
init_manager();
|
|
51652
|
+
init_persistence();
|
|
50333
51653
|
init_registry3();
|
|
50334
51654
|
PROVIDERS2 = ["anthropic", "openai", "github-copilot"];
|
|
50335
51655
|
PROVIDER_LABELS = {
|
|
@@ -50515,8 +51835,8 @@ var init_rpcHandlers = __esm({
|
|
|
50515
51835
|
${raw}`;
|
|
50516
51836
|
return `${header}: ${raw}`;
|
|
50517
51837
|
}, "formatBridgeText");
|
|
50518
|
-
runProcess = /* @__PURE__ */ __name((command, args, timeout = 2e4) => new Promise((
|
|
50519
|
-
const child =
|
|
51838
|
+
runProcess = /* @__PURE__ */ __name((command, args, timeout = 2e4) => new Promise((resolve6) => {
|
|
51839
|
+
const child = spawn6(command, args, { timeout, windowsHide: true });
|
|
50520
51840
|
let stdout = "";
|
|
50521
51841
|
let stderr = "";
|
|
50522
51842
|
let truncated = false;
|
|
@@ -50539,9 +51859,9 @@ ${raw}`;
|
|
|
50539
51859
|
if (truncated) {
|
|
50540
51860
|
stderr += "\n[output truncated to 1MB]";
|
|
50541
51861
|
}
|
|
50542
|
-
|
|
51862
|
+
resolve6({ code: code ?? -1, stderr, stdout });
|
|
50543
51863
|
});
|
|
50544
|
-
child.on("error", (err) =>
|
|
51864
|
+
child.on("error", (err) => resolve6({ code: -1, stderr: err.message, stdout }));
|
|
50545
51865
|
}), "runProcess");
|
|
50546
51866
|
contentToText = /* @__PURE__ */ __name((content) => {
|
|
50547
51867
|
if (typeof content === "string") return content;
|
|
@@ -50699,14 +52019,14 @@ var init_memoryMonitor = __esm({
|
|
|
50699
52019
|
});
|
|
50700
52020
|
|
|
50701
52021
|
// src/lib/openExternalUrl.ts
|
|
50702
|
-
import { spawn as
|
|
52022
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
50703
52023
|
import { platform } from "node:os";
|
|
50704
52024
|
function openExternalUrl(rawUrl, dependencies = {}) {
|
|
50705
52025
|
const url = parseSafeUrl(rawUrl);
|
|
50706
52026
|
if (!url) {
|
|
50707
52027
|
return false;
|
|
50708
52028
|
}
|
|
50709
|
-
const spawnFn = dependencies.spawn ??
|
|
52029
|
+
const spawnFn = dependencies.spawn ?? spawn7;
|
|
50710
52030
|
const platformId = dependencies.platform?.() ?? platform();
|
|
50711
52031
|
const command = openCommand(platformId);
|
|
50712
52032
|
if (!command) {
|
|
@@ -51496,7 +52816,7 @@ async function terminalParityHints(env2 = process.env, options) {
|
|
|
51496
52816
|
hints.push({
|
|
51497
52817
|
key: "remote",
|
|
51498
52818
|
tone: "warn",
|
|
51499
|
-
message: "SSH session detected \xB7 text clipboard can bridge via OSC52, but image clipboard and local screenshot paths still depend on the machine running
|
|
52819
|
+
message: "SSH session detected \xB7 text clipboard can bridge via OSC52, but image clipboard and local screenshot paths still depend on the machine running OpenJaw"
|
|
51500
52820
|
});
|
|
51501
52821
|
}
|
|
51502
52822
|
return hints;
|
|
@@ -51601,13 +52921,13 @@ var init_setup2 = __esm({
|
|
|
51601
52921
|
SETUP_REQUIRED_TITLE = "Setup Required";
|
|
51602
52922
|
buildSetupRequiredSections = /* @__PURE__ */ __name(() => [
|
|
51603
52923
|
{
|
|
51604
|
-
text: "
|
|
52924
|
+
text: "OpenJaw needs a model provider before the TUI can start a session. Run /connect to set up a provider, then /model to choose a model."
|
|
51605
52925
|
},
|
|
51606
52926
|
{
|
|
51607
52927
|
rows: [
|
|
51608
|
-
["/
|
|
51609
|
-
["/
|
|
51610
|
-
["Ctrl+C", "exit and
|
|
52928
|
+
["/connect", "set up provider credentials in-place"],
|
|
52929
|
+
["/model", "choose or switch model after connecting"],
|
|
52930
|
+
["Ctrl+C", "exit and restart OpenJaw after setup if needed"]
|
|
51611
52931
|
],
|
|
51612
52932
|
title: "Actions"
|
|
51613
52933
|
}
|
|
@@ -51910,7 +53230,7 @@ var init_reasoning2 = __esm({
|
|
|
51910
53230
|
});
|
|
51911
53231
|
|
|
51912
53232
|
// src/app/turnStore.ts
|
|
51913
|
-
import { atom as
|
|
53233
|
+
import { atom as atom6 } from "nanostores";
|
|
51914
53234
|
import { useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
51915
53235
|
var buildTurnState, $turnState, getTurnState, subscribeTurn, useTurnSelector, patchTurnState, toggleTodoCollapsed, archiveDoneTodos, archiveTodosAtTurnEnd, resetTurnState;
|
|
51916
53236
|
var init_turnStore = __esm({
|
|
@@ -51934,7 +53254,7 @@ var init_turnStore = __esm({
|
|
|
51934
53254
|
tools: [],
|
|
51935
53255
|
turnTrail: []
|
|
51936
53256
|
}), "buildTurnState");
|
|
51937
|
-
$turnState =
|
|
53257
|
+
$turnState = atom6(buildTurnState());
|
|
51938
53258
|
getTurnState = /* @__PURE__ */ __name(() => $turnState.get(), "getTurnState");
|
|
51939
53259
|
subscribeTurn = /* @__PURE__ */ __name((cb) => $turnState.listen(() => cb()), "subscribeTurn");
|
|
51940
53260
|
useTurnSelector = /* @__PURE__ */ __name((selector) => useSyncExternalStore4(
|
|
@@ -52534,7 +53854,9 @@ ${stripped}
|
|
|
52534
53854
|
...base,
|
|
52535
53855
|
apiCalls: p.api_calls ?? base.apiCalls,
|
|
52536
53856
|
costUsd: p.cost_usd ?? base.costUsd,
|
|
53857
|
+
currentStep: p.current_step ?? base.currentStep,
|
|
52537
53858
|
depth: p.depth ?? base.depth,
|
|
53859
|
+
details: p.details ?? base.details,
|
|
52538
53860
|
filesRead: p.files_read ?? base.filesRead,
|
|
52539
53861
|
filesWritten: p.files_written ?? base.filesWritten,
|
|
52540
53862
|
goal: p.goal || base.goal,
|
|
@@ -52548,6 +53870,9 @@ ${stripped}
|
|
|
52548
53870
|
taskCount: p.task_count ?? base.taskCount,
|
|
52549
53871
|
toolCount: p.tool_count ?? base.toolCount,
|
|
52550
53872
|
toolsets: p.toolsets ?? base.toolsets,
|
|
53873
|
+
verificationState: p.verification_state ?? base.verificationState,
|
|
53874
|
+
workflowId: p.workflow_id ?? base.workflowId,
|
|
53875
|
+
workerRole: p.worker_role ?? base.workerRole,
|
|
52551
53876
|
...patch(base)
|
|
52552
53877
|
};
|
|
52553
53878
|
const subagents = existing ? state.subagents.map((item) => item.id === id ? next : item) : [...state.subagents, next].sort((a, b) => a.depth - b.depth || a.index - b.index);
|
|
@@ -52634,7 +53959,7 @@ function createGatewayEventHandler(ctx) {
|
|
|
52634
53959
|
setTimeout(async () => {
|
|
52635
53960
|
let sid = getUiState().sid;
|
|
52636
53961
|
for (let i = 0; !sid && i < 40; i += 1) {
|
|
52637
|
-
await new Promise((
|
|
53962
|
+
await new Promise((resolve6) => setTimeout(resolve6, 100));
|
|
52638
53963
|
sid = getUiState().sid;
|
|
52639
53964
|
}
|
|
52640
53965
|
if (!sid) {
|
|
@@ -52981,6 +54306,26 @@ function createGatewayEventHandler(ctx) {
|
|
|
52981
54306
|
{ createIfMissing: false }
|
|
52982
54307
|
);
|
|
52983
54308
|
return;
|
|
54309
|
+
case "workflow.start":
|
|
54310
|
+
case "workflow.progress":
|
|
54311
|
+
setWorkflowSnapshot(ev.payload);
|
|
54312
|
+
return;
|
|
54313
|
+
case "workflow.complete": {
|
|
54314
|
+
setWorkflowSnapshot(ev.payload);
|
|
54315
|
+
const text = String(ev.payload?.summary ?? "").trim();
|
|
54316
|
+
if (text) {
|
|
54317
|
+
appendMessage({ role: "assistant", text });
|
|
54318
|
+
}
|
|
54319
|
+
return;
|
|
54320
|
+
}
|
|
54321
|
+
case "workflow.error": {
|
|
54322
|
+
setWorkflowSnapshot(ev.payload);
|
|
54323
|
+
const text = String(ev.payload?.summary ?? "").trim();
|
|
54324
|
+
if (text) {
|
|
54325
|
+
sys(text);
|
|
54326
|
+
}
|
|
54327
|
+
return;
|
|
54328
|
+
}
|
|
52984
54329
|
case "message.delta":
|
|
52985
54330
|
turnController.recordMessageDelta(ev.payload ?? {});
|
|
52986
54331
|
return;
|
|
@@ -53015,7 +54360,7 @@ function createGatewayEventHandler(ctx) {
|
|
|
53015
54360
|
}
|
|
53016
54361
|
};
|
|
53017
54362
|
}
|
|
53018
|
-
var NO_PROVIDER_RE, statusFromBusy, applySkin, dropBgTask,
|
|
54363
|
+
var NO_PROVIDER_RE, statusFromBusy, applySkin, dropBgTask, pushUnique2, pushThinking, pushNote, pushTool;
|
|
53019
54364
|
var init_createGatewayEventHandler = __esm({
|
|
53020
54365
|
"src/app/createGatewayEventHandler.ts"() {
|
|
53021
54366
|
"use strict";
|
|
@@ -53030,6 +54375,7 @@ var init_createGatewayEventHandler = __esm({
|
|
|
53030
54375
|
init_overlayStore();
|
|
53031
54376
|
init_turnController();
|
|
53032
54377
|
init_uiStore();
|
|
54378
|
+
init_workflowStore();
|
|
53033
54379
|
NO_PROVIDER_RE = /\bNo (?:LLM|inference) provider configured\b/i;
|
|
53034
54380
|
statusFromBusy = /* @__PURE__ */ __name(() => getUiState().busy ? "running\u2026" : "ready", "statusFromBusy");
|
|
53035
54381
|
applySkin = /* @__PURE__ */ __name((s) => patchUiState({
|
|
@@ -53047,10 +54393,10 @@ var init_createGatewayEventHandler = __esm({
|
|
|
53047
54393
|
next.delete(taskId);
|
|
53048
54394
|
return { ...state, bgTasks: next };
|
|
53049
54395
|
}), "dropBgTask");
|
|
53050
|
-
|
|
53051
|
-
pushThinking =
|
|
53052
|
-
pushNote =
|
|
53053
|
-
pushTool =
|
|
54396
|
+
pushUnique2 = /* @__PURE__ */ __name((max) => (xs, x) => xs.at(-1) === x ? xs : [...xs, x].slice(-max), "pushUnique");
|
|
54397
|
+
pushThinking = pushUnique2(6);
|
|
54398
|
+
pushNote = pushUnique2(6);
|
|
54399
|
+
pushTool = pushUnique2(8);
|
|
53054
54400
|
__name(createGatewayEventHandler, "createGatewayEventHandler");
|
|
53055
54401
|
}
|
|
53056
54402
|
});
|
|
@@ -53194,12 +54540,12 @@ var init_createSlashHandler = __esm({
|
|
|
53194
54540
|
});
|
|
53195
54541
|
|
|
53196
54542
|
// src/app/inputSelectionStore.ts
|
|
53197
|
-
import { atom as
|
|
54543
|
+
import { atom as atom7 } from "nanostores";
|
|
53198
54544
|
var $inputSelection, setInputSelection, getInputSelection;
|
|
53199
54545
|
var init_inputSelectionStore = __esm({
|
|
53200
54546
|
"src/app/inputSelectionStore.ts"() {
|
|
53201
54547
|
"use strict";
|
|
53202
|
-
$inputSelection =
|
|
54548
|
+
$inputSelection = atom7(null);
|
|
53203
54549
|
setInputSelection = /* @__PURE__ */ __name((next) => $inputSelection.set(next), "setInputSelection");
|
|
53204
54550
|
getInputSelection = /* @__PURE__ */ __name(() => $inputSelection.get(), "getInputSelection");
|
|
53205
54551
|
}
|
|
@@ -53360,21 +54706,21 @@ var init_useCompletion = __esm({
|
|
|
53360
54706
|
});
|
|
53361
54707
|
|
|
53362
54708
|
// src/lib/history.ts
|
|
53363
|
-
import { appendFileSync as appendFileSync4, existsSync as
|
|
53364
|
-
import { homedir as
|
|
53365
|
-
import { join as
|
|
54709
|
+
import { appendFileSync as appendFileSync4, existsSync as existsSync33, mkdirSync as mkdirSync19, readFileSync as readFileSync30 } from "node:fs";
|
|
54710
|
+
import { homedir as homedir30 } from "node:os";
|
|
54711
|
+
import { join as join44 } from "node:path";
|
|
53366
54712
|
function load() {
|
|
53367
54713
|
if (cache3) {
|
|
53368
54714
|
return cache3;
|
|
53369
54715
|
}
|
|
53370
54716
|
try {
|
|
53371
|
-
if (!
|
|
54717
|
+
if (!existsSync33(file)) {
|
|
53372
54718
|
cache3 = [];
|
|
53373
54719
|
return cache3;
|
|
53374
54720
|
}
|
|
53375
54721
|
const entries = [];
|
|
53376
54722
|
let current = [];
|
|
53377
|
-
for (const line of
|
|
54723
|
+
for (const line of readFileSync30(file, "utf8").split("\n")) {
|
|
53378
54724
|
if (line.startsWith("+")) {
|
|
53379
54725
|
current.push(line.slice(1));
|
|
53380
54726
|
} else if (current.length) {
|
|
@@ -53405,8 +54751,8 @@ function append(line) {
|
|
|
53405
54751
|
items.splice(0, items.length - MAX);
|
|
53406
54752
|
}
|
|
53407
54753
|
try {
|
|
53408
|
-
if (!
|
|
53409
|
-
|
|
54754
|
+
if (!existsSync33(dir)) {
|
|
54755
|
+
mkdirSync19(dir, { recursive: true });
|
|
53410
54756
|
}
|
|
53411
54757
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace("Z", "");
|
|
53412
54758
|
const encoded = trimmed.split("\n").map((l) => `+${l}`).join("\n");
|
|
@@ -53422,8 +54768,8 @@ var init_history = __esm({
|
|
|
53422
54768
|
"src/lib/history.ts"() {
|
|
53423
54769
|
"use strict";
|
|
53424
54770
|
MAX = 1e3;
|
|
53425
|
-
dir = process.env.OPENJAW_HOME ??
|
|
53426
|
-
file =
|
|
54771
|
+
dir = process.env.OPENJAW_HOME ?? join44(homedir30(), ".openjaw-agent");
|
|
54772
|
+
file = join44(dir, ".openjaw-agent_history");
|
|
53427
54773
|
cache3 = null;
|
|
53428
54774
|
__name(load, "load");
|
|
53429
54775
|
__name(append, "append");
|
|
@@ -53517,7 +54863,7 @@ var init_useQueue = __esm({
|
|
|
53517
54863
|
|
|
53518
54864
|
// src/lib/editor.ts
|
|
53519
54865
|
import { accessSync, constants } from "node:fs";
|
|
53520
|
-
import { delimiter, join as
|
|
54866
|
+
import { delimiter, join as join45 } from "node:path";
|
|
53521
54867
|
var FALLBACKS, isExecutable, resolveEditor;
|
|
53522
54868
|
var init_editor = __esm({
|
|
53523
54869
|
"src/lib/editor.ts"() {
|
|
@@ -53540,7 +54886,7 @@ var init_editor = __esm({
|
|
|
53540
54886
|
return ["notepad.exe"];
|
|
53541
54887
|
}
|
|
53542
54888
|
const dirs = (env2.PATH ?? "").split(delimiter).filter(Boolean);
|
|
53543
|
-
const found = FALLBACKS.flatMap((name) => dirs.map((d) =>
|
|
54889
|
+
const found = FALLBACKS.flatMap((name) => dirs.map((d) => join45(d, name))).find(isExecutable);
|
|
53544
54890
|
return [found ?? "vi"];
|
|
53545
54891
|
}, "resolveEditor");
|
|
53546
54892
|
}
|
|
@@ -53548,9 +54894,9 @@ var init_editor = __esm({
|
|
|
53548
54894
|
|
|
53549
54895
|
// src/app/useComposerState.ts
|
|
53550
54896
|
import { spawnSync } from "node:child_process";
|
|
53551
|
-
import { mkdtempSync, readFileSync as
|
|
54897
|
+
import { mkdtempSync, readFileSync as readFileSync31, rmSync as rmSync2, writeFileSync as writeFileSync21 } from "node:fs";
|
|
53552
54898
|
import { tmpdir as tmpdir12 } from "node:os";
|
|
53553
|
-
import { join as
|
|
54899
|
+
import { join as join46 } from "node:path";
|
|
53554
54900
|
import { useStore } from "@nanostores/react";
|
|
53555
54901
|
import { useCallback as useCallback7, useMemo as useMemo6, useState as useState12 } from "react";
|
|
53556
54902
|
function insertAtCursor(value, cursor, text) {
|
|
@@ -53707,10 +55053,10 @@ function useComposerState({
|
|
|
53707
55053
|
[handleResolvedPaste, onClipboardPaste, querier]
|
|
53708
55054
|
);
|
|
53709
55055
|
const openEditor = useCallback7(async () => {
|
|
53710
|
-
const dir2 = mkdtempSync(
|
|
53711
|
-
const file2 =
|
|
55056
|
+
const dir2 = mkdtempSync(join46(tmpdir12(), "hermes-"));
|
|
55057
|
+
const file2 = join46(dir2, "prompt.md");
|
|
53712
55058
|
const [cmd, ...args] = resolveEditor();
|
|
53713
|
-
|
|
55059
|
+
writeFileSync21(file2, [...inputBuf, input].join("\n"));
|
|
53714
55060
|
let exitCode = null;
|
|
53715
55061
|
await withInkSuspended(async () => {
|
|
53716
55062
|
exitCode = spawnSync(cmd, [...args, file2], { stdio: "inherit" }).status;
|
|
@@ -53719,7 +55065,7 @@ function useComposerState({
|
|
|
53719
55065
|
if (exitCode !== 0) {
|
|
53720
55066
|
return;
|
|
53721
55067
|
}
|
|
53722
|
-
const text =
|
|
55068
|
+
const text = readFileSync31(file2, "utf8").trimEnd();
|
|
53723
55069
|
if (!text) {
|
|
53724
55070
|
return;
|
|
53725
55071
|
}
|
|
@@ -54137,11 +55483,11 @@ function applyVoiceRecordResponse(response, starting, voice, sys) {
|
|
|
54137
55483
|
}
|
|
54138
55484
|
}
|
|
54139
55485
|
function useInputHandlers(ctx) {
|
|
54140
|
-
const { actions, composer, gateway, terminal, voice, wheelStep } = ctx;
|
|
55486
|
+
const { actions, composer, gateway, terminal: terminal2, voice, wheelStep } = ctx;
|
|
54141
55487
|
const { actions: cActions, refs: cRefs, state: cState } = composer;
|
|
54142
55488
|
const overlay = useStore2($overlayState);
|
|
54143
55489
|
const isBlocked = useStore2($isBlocked);
|
|
54144
|
-
const pagerPageSize = Math.max(5, (
|
|
55490
|
+
const pagerPageSize = Math.max(5, (terminal2.stdout?.rows ?? 24) - 6);
|
|
54145
55491
|
const scrollIdleTimer = useRef10(null);
|
|
54146
55492
|
const wheelAccelRef = useRef10(initWheelAccelForHost());
|
|
54147
55493
|
const precisionWheelRef = useRef10(initPrecisionWheel());
|
|
@@ -54156,13 +55502,13 @@ function useInputHandlers(ctx) {
|
|
|
54156
55502
|
turnController.relaxStreaming();
|
|
54157
55503
|
}, TYPING_IDLE_MS);
|
|
54158
55504
|
}
|
|
54159
|
-
|
|
55505
|
+
terminal2.scrollWithSelection(delta);
|
|
54160
55506
|
}, "scrollTranscript");
|
|
54161
55507
|
const copySelection = /* @__PURE__ */ __name(() => {
|
|
54162
|
-
|
|
55508
|
+
terminal2.selection.copySelection();
|
|
54163
55509
|
}, "copySelection");
|
|
54164
55510
|
const clearSelection2 = /* @__PURE__ */ __name(() => {
|
|
54165
|
-
|
|
55511
|
+
terminal2.selection.clearSelection();
|
|
54166
55512
|
}, "clearSelection");
|
|
54167
55513
|
const cancelOverlayFromCtrlC = /* @__PURE__ */ __name(() => {
|
|
54168
55514
|
if (overlay.clarify) {
|
|
@@ -54361,7 +55707,7 @@ function useInputHandlers(ctx) {
|
|
|
54361
55707
|
return scrollTranscript(1);
|
|
54362
55708
|
}
|
|
54363
55709
|
if (key.pageUp || key.pageDown) {
|
|
54364
|
-
const viewport =
|
|
55710
|
+
const viewport = terminal2.scrollRef.current?.getViewportHeight() ?? Math.max(6, (terminal2.stdout?.rows ?? 24) - 8);
|
|
54365
55711
|
const step = Math.max(4, Math.floor(viewport / 2));
|
|
54366
55712
|
return scrollTranscript(key.pageUp ? -step : step);
|
|
54367
55713
|
}
|
|
@@ -54371,7 +55717,7 @@ function useInputHandlers(ctx) {
|
|
|
54371
55717
|
if (key.escape && cState.queueEditIdx !== null) {
|
|
54372
55718
|
return cActions.clearIn();
|
|
54373
55719
|
}
|
|
54374
|
-
if (key.escape &&
|
|
55720
|
+
if (key.escape && terminal2.hasSelection) {
|
|
54375
55721
|
return clearSelection2();
|
|
54376
55722
|
}
|
|
54377
55723
|
if (key.escape && live.focusedPane === "transcript") {
|
|
@@ -54397,7 +55743,7 @@ function useInputHandlers(ctx) {
|
|
|
54397
55743
|
}
|
|
54398
55744
|
}
|
|
54399
55745
|
if (isCopyShortcut(key, ch)) {
|
|
54400
|
-
if (
|
|
55746
|
+
if (terminal2.hasSelection) {
|
|
54401
55747
|
return copySelection();
|
|
54402
55748
|
}
|
|
54403
55749
|
const inputSel = getInputSelection();
|
|
@@ -54432,7 +55778,7 @@ function useInputHandlers(ctx) {
|
|
|
54432
55778
|
}
|
|
54433
55779
|
if (isAction(key, ch, "l")) {
|
|
54434
55780
|
clearSelection2();
|
|
54435
|
-
forceRedraw(
|
|
55781
|
+
forceRedraw(terminal2.stdout ?? process.stdout);
|
|
54436
55782
|
return;
|
|
54437
55783
|
}
|
|
54438
55784
|
if (isVoiceToggleKey(key, ch, voice.recordKey)) {
|
|
@@ -54582,7 +55928,7 @@ var init_useLongRunToolCharms = __esm({
|
|
|
54582
55928
|
});
|
|
54583
55929
|
|
|
54584
55930
|
// src/app/useSessionLifecycle.ts
|
|
54585
|
-
import { writeFileSync as
|
|
55931
|
+
import { writeFileSync as writeFileSync22 } from "node:fs";
|
|
54586
55932
|
import { useCallback as useCallback8 } from "react";
|
|
54587
55933
|
function useSessionLifecycle(opts) {
|
|
54588
55934
|
const {
|
|
@@ -54765,7 +56111,7 @@ var init_useSessionLifecycle = __esm({
|
|
|
54765
56111
|
return;
|
|
54766
56112
|
}
|
|
54767
56113
|
try {
|
|
54768
|
-
|
|
56114
|
+
writeFileSync22(file2, JSON.stringify({ session_id: sessionId }), { mode: 384 });
|
|
54769
56115
|
} catch {
|
|
54770
56116
|
}
|
|
54771
56117
|
}, "writeActiveSessionFile");
|
|
@@ -55622,8 +56968,13 @@ function useMainApp(gw) {
|
|
|
55622
56968
|
);
|
|
55623
56969
|
const onModelSelect = useCallback10((value) => {
|
|
55624
56970
|
patchOverlayState({ modelPicker: false });
|
|
56971
|
+
if (value === "__openjaw_connect_complete__") {
|
|
56972
|
+
sys("provider connected \u2014 starting OpenJaw session\u2026");
|
|
56973
|
+
session.newSession();
|
|
56974
|
+
return;
|
|
56975
|
+
}
|
|
55625
56976
|
slashRef.current(`/model ${value}`);
|
|
55626
|
-
}, []);
|
|
56977
|
+
}, [session, sys]);
|
|
55627
56978
|
const hasReasoning = useTurnSelector((state) => Boolean(state.reasoning.trim()));
|
|
55628
56979
|
const anyPanelVisible = SECTION_NAMES.some(
|
|
55629
56980
|
(s) => sectionMode(s, ui.detailsMode, ui.sections, ui.detailsModeCommandOverride) !== "hidden"
|
|
@@ -55795,9 +57146,9 @@ __export(perfPane_exports, {
|
|
|
55795
57146
|
PerfPane: () => PerfPane,
|
|
55796
57147
|
logFrameEvent: () => logFrameEvent
|
|
55797
57148
|
});
|
|
55798
|
-
import { appendFileSync as appendFileSync5, mkdirSync as
|
|
55799
|
-
import { homedir as
|
|
55800
|
-
import { dirname as dirname7, join as
|
|
57149
|
+
import { appendFileSync as appendFileSync5, mkdirSync as mkdirSync20 } from "node:fs";
|
|
57150
|
+
import { homedir as homedir31 } from "node:os";
|
|
57151
|
+
import { dirname as dirname7, join as join47 } from "node:path";
|
|
55801
57152
|
import { Profiler } from "react";
|
|
55802
57153
|
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
55803
57154
|
function PerfPane({ children, id }) {
|
|
@@ -55813,13 +57164,13 @@ var init_perfPane = __esm({
|
|
|
55813
57164
|
init_entry_exports();
|
|
55814
57165
|
ENABLED = /^(?:1|true|yes|on)$/i.test((process.env.OPENJAW_DEV_PERF ?? "").trim());
|
|
55815
57166
|
THRESHOLD_MS = Number(process.env.OPENJAW_DEV_PERF_MS ?? "2") || 0;
|
|
55816
|
-
LOG_PATH2 = process.env.OPENJAW_DEV_PERF_LOG?.trim() ||
|
|
57167
|
+
LOG_PATH2 = process.env.OPENJAW_DEV_PERF_LOG?.trim() || join47(homedir31(), ".openjaw-agent", "perf.log");
|
|
55817
57168
|
logReady = false;
|
|
55818
57169
|
writeRow = /* @__PURE__ */ __name((row) => {
|
|
55819
57170
|
if (!logReady) {
|
|
55820
57171
|
logReady = true;
|
|
55821
57172
|
try {
|
|
55822
|
-
|
|
57173
|
+
mkdirSync20(dirname7(LOG_PATH2), { recursive: true });
|
|
55823
57174
|
} catch {
|
|
55824
57175
|
}
|
|
55825
57176
|
}
|
|
@@ -56072,6 +57423,10 @@ function Detail({ id, node, t }) {
|
|
|
56072
57423
|
] }),
|
|
56073
57424
|
/* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", marginTop: 1, children: [
|
|
56074
57425
|
/* @__PURE__ */ jsx18(Field, { name: "depth", t, value: `${item.depth} \xB7 ${item.status}` }),
|
|
57426
|
+
item.workflowId ? /* @__PURE__ */ jsx18(Field, { name: "workflow", t, value: item.workflowId }) : null,
|
|
57427
|
+
item.workerRole ? /* @__PURE__ */ jsx18(Field, { name: "role", t, value: item.workerRole }) : null,
|
|
57428
|
+
item.verificationState ? /* @__PURE__ */ jsx18(Field, { name: "verification", t, value: item.verificationState }) : null,
|
|
57429
|
+
item.currentStep ? /* @__PURE__ */ jsx18(Field, { name: "current", t, value: item.currentStep }) : null,
|
|
56075
57430
|
item.model ? /* @__PURE__ */ jsx18(Field, { name: "model", t, value: item.model }) : null,
|
|
56076
57431
|
item.toolsets?.length ? /* @__PURE__ */ jsx18(Field, { name: "toolsets", t, value: item.toolsets.join(", ") }) : null,
|
|
56077
57432
|
/* @__PURE__ */ jsx18(Field, { name: "tools", t, value: `${item.toolCount ?? 0} (subtree ${agg.totalTools})` }),
|
|
@@ -56146,7 +57501,8 @@ function Detail({ id, node, t }) {
|
|
|
56146
57501
|
" ",
|
|
56147
57502
|
line
|
|
56148
57503
|
] }, i)) }) : null,
|
|
56149
|
-
item.summary ? /* @__PURE__ */ jsx18(OverlaySection, { defaultOpen: true, t, title: "Summary", children: /* @__PURE__ */ jsx18(Text9, { color: t.color.text, wrap: "wrap", children: item.summary }) }) : null
|
|
57504
|
+
item.summary ? /* @__PURE__ */ jsx18(OverlaySection, { defaultOpen: true, t, title: "Summary", children: /* @__PURE__ */ jsx18(Text9, { color: t.color.text, wrap: "wrap", children: item.summary }) }) : null,
|
|
57505
|
+
item.details && item.details !== item.summary ? /* @__PURE__ */ jsx18(OverlaySection, { defaultOpen: true, t, title: "Details", children: /* @__PURE__ */ jsx18(Text9, { color: t.color.text, wrap: "wrap", children: item.details }) }) : null
|
|
56150
57506
|
] });
|
|
56151
57507
|
}
|
|
56152
57508
|
function ListRow({
|
|
@@ -56246,12 +57602,23 @@ function DiffView({
|
|
|
56246
57602
|
] })
|
|
56247
57603
|
] });
|
|
56248
57604
|
}
|
|
56249
|
-
function AgentsOverlay({ gw, initialHistoryIndex = 0, onClose, t }) {
|
|
56250
|
-
const
|
|
57605
|
+
function AgentsOverlay({ gw, initialHistoryIndex = 0, onClose, t, workflowId = null }) {
|
|
57606
|
+
const allLiveSubagents = useTurnSelector((state) => state.subagents);
|
|
56251
57607
|
const delegation = useStore4($delegationState);
|
|
56252
57608
|
const history = useStore4($spawnHistory);
|
|
56253
57609
|
const diffPair = useStore4($spawnDiff);
|
|
57610
|
+
const workflowSnapshots = useStore4($workflowSnapshots);
|
|
56254
57611
|
const { stdout } = useStdout();
|
|
57612
|
+
const liveSubagents = workflowId ? allLiveSubagents.filter((item) => item.workflowId === workflowId) : allLiveSubagents;
|
|
57613
|
+
const workflowSnapshot = workflowId ? workflowSnapshots[workflowId] ?? null : null;
|
|
57614
|
+
const workflowReplaySnapshot = workflowSnapshot && liveSubagents.length === 0 ? {
|
|
57615
|
+
finishedAt: workflowSnapshot.finishedAt ?? Date.now(),
|
|
57616
|
+
id: workflowSnapshot.id,
|
|
57617
|
+
label: workflowSnapshot.goal,
|
|
57618
|
+
sessionId: workflowSnapshot.id,
|
|
57619
|
+
startedAt: workflowSnapshot.startedAt,
|
|
57620
|
+
subagents: workflowSnapshot.workers
|
|
57621
|
+
} : null;
|
|
56255
57622
|
const [historyIndex, setHistoryIndex] = useState14(
|
|
56256
57623
|
() => Math.max(0, Math.min(history.length, Math.floor(initialHistoryIndex)))
|
|
56257
57624
|
);
|
|
@@ -56263,9 +57630,9 @@ function AgentsOverlay({ gw, initialHistoryIndex = 0, onClose, t }) {
|
|
|
56263
57630
|
const [mode, setMode] = useState14("list");
|
|
56264
57631
|
const detailScrollRef = useRef14(null);
|
|
56265
57632
|
const prevLiveCountRef = useRef14(liveSubagents.length);
|
|
56266
|
-
const activeSnapshot = historyIndex > 0 ? history[historyIndex - 1] : null;
|
|
56267
|
-
const justFinishedSnapshot = historyIndex === 0 && liveSubagents.length === 0 ? history[0] ?? null : null;
|
|
56268
|
-
const effectiveSnapshot = activeSnapshot ?? justFinishedSnapshot;
|
|
57633
|
+
const activeSnapshot = !workflowId && historyIndex > 0 ? history[historyIndex - 1] : null;
|
|
57634
|
+
const justFinishedSnapshot = !workflowId && historyIndex === 0 && liveSubagents.length === 0 ? history[0] ?? null : null;
|
|
57635
|
+
const effectiveSnapshot = workflowReplaySnapshot ?? activeSnapshot ?? justFinishedSnapshot;
|
|
56269
57636
|
const replayMode = effectiveSnapshot != null;
|
|
56270
57637
|
const subagents = replayMode ? effectiveSnapshot.subagents : liveSubagents;
|
|
56271
57638
|
const tree = useMemo8(() => buildSubagentTree(subagents), [subagents]);
|
|
@@ -56434,7 +57801,7 @@ function AgentsOverlay({ gw, initialHistoryIndex = 0, onClose, t }) {
|
|
|
56434
57801
|
const capsLabel = delegation.maxSpawnDepth ? `caps d${delegation.maxSpawnDepth}/${delegation.maxConcurrentChildren ?? "?"}` : "";
|
|
56435
57802
|
const title = replayMode && effectiveSnapshot ? `${historyIndex > 0 ? `Replay ${historyIndex}/${history.length}` : "Last turn"} \xB7 finished ${new Date(
|
|
56436
57803
|
effectiveSnapshot.finishedAt
|
|
56437
|
-
).toLocaleTimeString()}` : `Spawn tree${delegation.paused ? " \xB7 \u23F8 paused" : ""}`;
|
|
57804
|
+
).toLocaleTimeString()}` : workflowId ? `Workflow ${workflowId}${delegation.paused ? " \xB7 \u23F8 paused" : ""}` : `Spawn tree${delegation.paused ? " \xB7 \u23F8 paused" : ""}`;
|
|
56438
57805
|
const metaLine = [formatSummary(totals), spark, capsLabel, mix2 ? `\xB7 ${mix2}` : ""].filter(Boolean).join(" ");
|
|
56439
57806
|
const controlsHint = replayMode ? " \xB7 controls locked" : ` \xB7 x kill \xB7 X subtree \xB7 p ${delegation.paused ? "resume" : "pause"}`;
|
|
56440
57807
|
if (diffPair) {
|
|
@@ -56448,7 +57815,7 @@ function AgentsOverlay({ gw, initialHistoryIndex = 0, onClose, t }) {
|
|
|
56448
57815
|
metaLine
|
|
56449
57816
|
] }) : null
|
|
56450
57817
|
] }) }),
|
|
56451
|
-
rows.length === 0 ? /* @__PURE__ */ jsx18(Box_default, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx18(Text9, { color: t.color.muted, children: "No subagents this turn. Trigger delegate_task to populate the tree." }) }) : mode === "list" ? /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, flexShrink: 1, minHeight: 0, children: [
|
|
57818
|
+
rows.length === 0 ? /* @__PURE__ */ jsx18(Box_default, { flexDirection: "column", flexGrow: 1, children: /* @__PURE__ */ jsx18(Text9, { color: t.color.muted, children: workflowId ? "No workers for this workflow yet." : "No subagents this turn. Trigger delegate_task to populate the tree." }) }) : mode === "list" ? /* @__PURE__ */ jsxs9(Box_default, { flexDirection: "column", flexGrow: 1, flexShrink: 1, minHeight: 0, children: [
|
|
56452
57819
|
/* @__PURE__ */ jsx18(GanttStrip, { cols, cursor, flatNodes: rows, maxRows: 6, now: now2, t }),
|
|
56453
57820
|
/* @__PURE__ */ jsx18(Box_default, { flexDirection: "column", flexGrow: 0, flexShrink: 0, overflow: "hidden", children: rows.slice(listWindowStart, listWindowStart + rowsH).map((node, i) => /* @__PURE__ */ jsx18(
|
|
56454
57821
|
ListRow,
|
|
@@ -56494,6 +57861,7 @@ var init_agentsOverlay = __esm({
|
|
|
56494
57861
|
init_overlayStore();
|
|
56495
57862
|
init_spawnHistoryStore();
|
|
56496
57863
|
init_turnStore();
|
|
57864
|
+
init_workflowStore();
|
|
56497
57865
|
init_rpc();
|
|
56498
57866
|
init_subagentTree();
|
|
56499
57867
|
init_text();
|
|
@@ -58422,7 +59790,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
|
|
|
58422
59790
|
refreshProviders();
|
|
58423
59791
|
}
|
|
58424
59792
|
if (mode === "connect") {
|
|
58425
|
-
|
|
59793
|
+
onSelect("__openjaw_connect_complete__");
|
|
58426
59794
|
return;
|
|
58427
59795
|
}
|
|
58428
59796
|
setStage("model");
|
|
@@ -58518,7 +59886,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
|
|
|
58518
59886
|
setKeyInput("");
|
|
58519
59887
|
setKeySaving(false);
|
|
58520
59888
|
if (mode === "connect") {
|
|
58521
|
-
|
|
59889
|
+
onSelect("__openjaw_connect_complete__");
|
|
58522
59890
|
return;
|
|
58523
59891
|
}
|
|
58524
59892
|
setStage("model");
|
|
@@ -58557,7 +59925,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
|
|
|
58557
59925
|
if (r?.disconnected) {
|
|
58558
59926
|
setProviders(
|
|
58559
59927
|
(prev) => prev.map(
|
|
58560
|
-
(p) => p.slug === provider.slug ? { ...p, authenticated: false, models: [], model_options: [], total_models: 0, warning: p.key_env ? `paste ${p.key_env} to activate` : "run
|
|
59928
|
+
(p) => p.slug === provider.slug ? { ...p, authenticated: false, models: [], model_options: [], total_models: 0, warning: p.key_env ? `paste ${p.key_env} to activate` : "run /connect to configure" } : p
|
|
58561
59929
|
)
|
|
58562
59930
|
);
|
|
58563
59931
|
}
|
|
@@ -58605,7 +59973,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
|
|
|
58605
59973
|
return;
|
|
58606
59974
|
}
|
|
58607
59975
|
if (mode === "connect") {
|
|
58608
|
-
|
|
59976
|
+
onSelect("__openjaw_connect_complete__");
|
|
58609
59977
|
return;
|
|
58610
59978
|
}
|
|
58611
59979
|
setStage("model");
|
|
@@ -58693,7 +60061,7 @@ function ModelPicker2({ gw, mode = "switch", onCancel, onSelect, sessionId, t })
|
|
|
58693
60061
|
"Configure ",
|
|
58694
60062
|
provider.name
|
|
58695
60063
|
] }),
|
|
58696
|
-
/* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "Paste your API key below (saved to
|
|
60064
|
+
/* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: "Paste your API key below (saved to OpenJaw credentials)" }),
|
|
58697
60065
|
/* @__PURE__ */ jsx25(Text9, { color: t.color.muted, wrap: "truncate-end", children: " " }),
|
|
58698
60066
|
/* @__PURE__ */ jsxs13(Text9, { color: t.color.muted, wrap: "truncate-end", children: [
|
|
58699
60067
|
provider.key_env,
|
|
@@ -59889,14 +61257,14 @@ __export(fpsStore_exports, {
|
|
|
59889
61257
|
$fpsState: () => $fpsState,
|
|
59890
61258
|
trackFrame: () => trackFrame
|
|
59891
61259
|
});
|
|
59892
|
-
import { atom as
|
|
61260
|
+
import { atom as atom8 } from "nanostores";
|
|
59893
61261
|
var WINDOW_SIZE, $fpsState, timestamps, totalFrames, trackFrame;
|
|
59894
61262
|
var init_fpsStore = __esm({
|
|
59895
61263
|
"src/lib/fpsStore.ts"() {
|
|
59896
61264
|
"use strict";
|
|
59897
61265
|
init_env();
|
|
59898
61266
|
WINDOW_SIZE = 30;
|
|
59899
|
-
$fpsState =
|
|
61267
|
+
$fpsState = atom8({ fps: 0, lastDurationMs: 0, totalFrames: 0 });
|
|
59900
61268
|
timestamps = [];
|
|
59901
61269
|
totalFrames = 0;
|
|
59902
61270
|
trackFrame = SHOW_FPS ? (durationMs) => {
|
|
@@ -61060,7 +62428,7 @@ var init_mathUnicode = __esm({
|
|
|
61060
62428
|
|
|
61061
62429
|
// src/lib/syntax.ts
|
|
61062
62430
|
function highlightLine(line, lang, t) {
|
|
61063
|
-
const spec =
|
|
62431
|
+
const spec = resolve5(lang);
|
|
61064
62432
|
if (!spec) {
|
|
61065
62433
|
return [["", line]];
|
|
61066
62434
|
}
|
|
@@ -61092,7 +62460,7 @@ function highlightLine(line, lang, t) {
|
|
|
61092
62460
|
}
|
|
61093
62461
|
return tokens;
|
|
61094
62462
|
}
|
|
61095
|
-
var KW, TS, PY, SH, GO, RUST, SQL, LANGS, ALIAS,
|
|
62463
|
+
var KW, TS, PY, SH, GO, RUST, SQL, LANGS, ALIAS, resolve5, isHighlightable, TOKEN_RE;
|
|
61096
62464
|
var init_syntax = __esm({
|
|
61097
62465
|
"src/lib/syntax.ts"() {
|
|
61098
62466
|
"use strict";
|
|
@@ -61146,8 +62514,8 @@ var init_syntax = __esm({
|
|
|
61146
62514
|
yml: "yaml",
|
|
61147
62515
|
zsh: "sh"
|
|
61148
62516
|
};
|
|
61149
|
-
|
|
61150
|
-
isHighlightable = /* @__PURE__ */ __name((lang) =>
|
|
62517
|
+
resolve5 = /* @__PURE__ */ __name((lang) => LANGS[ALIAS[lang] ?? lang] ?? null, "resolve");
|
|
62518
|
+
isHighlightable = /* @__PURE__ */ __name((lang) => resolve5(lang) !== null, "isHighlightable");
|
|
61151
62519
|
TOKEN_RE = /'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*`|\b\d+(?:\.\d+)?\b|[A-Za-z_$][\w$]*/g;
|
|
61152
62520
|
__name(highlightLine, "highlightLine");
|
|
61153
62521
|
}
|
|
@@ -63271,8 +64639,9 @@ var init_appLayout = __esm({
|
|
|
63271
64639
|
{
|
|
63272
64640
|
gw,
|
|
63273
64641
|
initialHistoryIndex: overlay.agentsInitialHistoryIndex,
|
|
63274
|
-
onClose: () => patchOverlayState({ agents: false, agentsInitialHistoryIndex: 0 }),
|
|
63275
|
-
t: ui.theme
|
|
64642
|
+
onClose: () => patchOverlayState({ agents: false, agentsInitialHistoryIndex: 0, agentsWorkflowId: null }),
|
|
64643
|
+
t: ui.theme,
|
|
64644
|
+
workflowId: overlay.agentsWorkflowId
|
|
63276
64645
|
}
|
|
63277
64646
|
);
|
|
63278
64647
|
}, "AgentsOverlayPane"));
|