newmark-agent 0.4.6 → 0.4.7
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/assets/app-icon-dark.svg +6 -0
- package/dist/assets/app-icon-dark.svg +6 -0
- package/dist/cli-commands.js +2 -1
- package/dist/cli-discovery.js +2 -0
- package/dist/conversation-utility-host.bundle.cjs +291 -149
- package/dist/core/agent.d.ts +38 -4
- package/dist/core/agent.js +231 -49
- package/dist/core/agentKernelRunner.d.ts +3 -0
- package/dist/core/agentKernelRunner.js +49 -83
- package/dist/core/config.js +1 -1
- package/dist/core/installUpdate.js +11 -8
- package/dist/core/mobilePairing.d.ts +1 -0
- package/dist/core/mobilePairing.js +15 -1
- package/dist/core/subagent.d.ts +10 -3
- package/dist/core/subagent.js +23 -8
- package/dist/core/toolPolicy.js +11 -3
- package/dist/launcher.js +14 -11
- package/dist/main.js +15 -3
- package/dist/providers/chat-completions.adapter.js +6 -2
- package/dist/providers/responses.adapter.js +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +543 -17
- package/dist/toolchain/registry-seeder.js +3 -1
- package/dist/tools/index.js +11 -5
- package/dist/tools/nativeTools.js +1 -1
- package/dist/ui/index.html +88 -101
- package/dist/wsl-agent-host.bundle.cjs +291 -149
- package/package.json +3 -1
|
@@ -327567,7 +327567,7 @@ var NATIVE_TOOL_CATALOG = [
|
|
|
327567
327567
|
{ name: "pdf_read", label: "PDF read", description: "Read PDF text and render scanned pages through vision before local OCR fallback.", category: "core", defaultEnabled: true, protected: true, availability: "mode-scoped" },
|
|
327568
327568
|
{ name: "terminal_takeover", label: "Terminal takeover", description: "Maintain a persistent Agent-controlled shell session.", category: "desktop", defaultEnabled: true },
|
|
327569
327569
|
{ name: "ssh_workspace", label: "OpenSSH workspace", description: "Manage native OpenSSH connections and link remote workspaces by PC_Hash.", category: "ssh", defaultEnabled: true },
|
|
327570
|
-
{ name: "
|
|
327570
|
+
{ name: "SubAgent", label: "SubAgent", description: "Create one real same-conversation peer SubAgent.", category: "agent", defaultEnabled: true, protected: true, availability: "mode-scoped" },
|
|
327571
327571
|
{ name: "subagent_list", label: "Subagent list", description: "List same-conversation peer agents.", category: "agent", defaultEnabled: true, protected: true, availability: "mode-scoped" },
|
|
327572
327572
|
{ name: "subagent_read", label: "Subagent read", description: "Read bounded status, feedback, result, queue, and mailbox summaries for a same-conversation peer.", category: "agent", defaultEnabled: true, protected: true, availability: "mode-scoped" },
|
|
327573
327573
|
{ name: "subagent_send", label: "Subagent send", description: "Persist a message to a peer mailbox.", category: "agent", defaultEnabled: true, protected: true, availability: "mode-scoped" },
|
|
@@ -328319,7 +328319,7 @@ function defaultConfig() {
|
|
|
328319
328319
|
auto_archive_on_close: { _description: "Auto archive on close", _type: "boolean", value: true }
|
|
328320
328320
|
},
|
|
328321
328321
|
remote: {
|
|
328322
|
-
touch_enabled: { _description: "Allow mobile devices to reach this Newmark instance
|
|
328322
|
+
touch_enabled: { _description: "Allow mobile devices to reach this Newmark instance on the same LAN / Tailscale network", _type: "boolean", value: true }
|
|
328323
328323
|
},
|
|
328324
328324
|
models: {
|
|
328325
328325
|
providers: { _description: "LLM providers", _type: "array", value: [] },
|
|
@@ -328867,14 +328867,18 @@ var ChatCompletionsAdapter = class {
|
|
|
328867
328867
|
...request.systemPrompt ? [{ role: CHAT_SYSTEM_ROLE, content: request.systemPrompt }] : [],
|
|
328868
328868
|
...openAIChatMessages(request.messages)
|
|
328869
328869
|
];
|
|
328870
|
+
const tools = this.serializeTools(request.tools);
|
|
328870
328871
|
const body = {
|
|
328871
328872
|
model: request.model,
|
|
328872
328873
|
messages,
|
|
328873
328874
|
temperature: request.temperature,
|
|
328874
|
-
max_tokens: request.maxOutputTokens
|
|
328875
|
-
tools: this.serializeTools(request.tools),
|
|
328876
|
-
tool_choice: "auto"
|
|
328875
|
+
max_tokens: request.maxOutputTokens
|
|
328877
328876
|
};
|
|
328877
|
+
if (tools.length) {
|
|
328878
|
+
body.tools = tools;
|
|
328879
|
+
body.tool_choice = "auto";
|
|
328880
|
+
body.parallel_tool_calls = true;
|
|
328881
|
+
}
|
|
328878
328882
|
if (request.reasoningEffort) body.reasoning_effort = request.reasoningEffort;
|
|
328879
328883
|
if (request.sessionId) body.session_id = request.sessionId;
|
|
328880
328884
|
const base2 = request.baseUrl.replace(/\/+$/, "");
|
|
@@ -329128,6 +329132,7 @@ var ResponsesAdapter = class {
|
|
|
329128
329132
|
if (tools.length) {
|
|
329129
329133
|
body.tools = tools;
|
|
329130
329134
|
body.tool_choice = "auto";
|
|
329135
|
+
body.parallel_tool_calls = true;
|
|
329131
329136
|
}
|
|
329132
329137
|
const base2 = request.baseUrl.replace(/\/+$/, "");
|
|
329133
329138
|
return {
|
|
@@ -335765,6 +335770,10 @@ var MODE_SCOPED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
335765
335770
|
"task_read",
|
|
335766
335771
|
"task_create",
|
|
335767
335772
|
"question",
|
|
335773
|
+
"SubAgent",
|
|
335774
|
+
"subagent_create",
|
|
335775
|
+
// Legacy runtime alias. It is no longer published to models because its
|
|
335776
|
+
// generic name collides with the persistent task checklist.
|
|
335768
335777
|
"task",
|
|
335769
335778
|
"subagent_list",
|
|
335770
335779
|
"subagent_read",
|
|
@@ -335798,6 +335807,8 @@ var PLAN_READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
335798
335807
|
"skill",
|
|
335799
335808
|
"linked_plan",
|
|
335800
335809
|
"build_history_query",
|
|
335810
|
+
"SubAgent",
|
|
335811
|
+
"subagent_create",
|
|
335801
335812
|
"task",
|
|
335802
335813
|
"subagent_list",
|
|
335803
335814
|
"subagent_read",
|
|
@@ -335821,7 +335832,8 @@ var CONCURRENCY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
335821
335832
|
"web_fetch",
|
|
335822
335833
|
"git_status",
|
|
335823
335834
|
"file_audit",
|
|
335824
|
-
"repo_security_audit"
|
|
335835
|
+
"repo_security_audit",
|
|
335836
|
+
"SubAgent"
|
|
335825
335837
|
]);
|
|
335826
335838
|
function isConcurrencySafeTool(name50, riskLevel) {
|
|
335827
335839
|
const toolName = String(name50 || "").trim();
|
|
@@ -336985,7 +336997,7 @@ var ToolExecutor = class {
|
|
|
336985
336997
|
remote_root: { type: "string" },
|
|
336986
336998
|
remote_path: { type: "string" }
|
|
336987
336999
|
}, ["action"]),
|
|
336988
|
-
t3("
|
|
337000
|
+
t3("SubAgent", "Create one real same-conversation peer SubAgent and return immediately. This tool is only for delegation; never use it to create or update the conversation task checklist (use task_create for that). Creation is bound to the currently running Build Block: non-Ultra intelligence has a hard ceiling of 4 active peers for that Build, Ultra has 16, and excess calls terminate without creating or queueing a record. The peer has a stable human-readable name plus a separate exact id. Pass model to select an exact configured model deployment (deployment:providerId:modelId or an unambiguous provider/model name). When model is omitted, the peer inherits the parent Agent's currently resolved model deployment. Plan mode peers are forced to Plan.", { name: { type: "string", description: "Human-readable SubAgent name shown in the monitoring sidebar. Omit only when a named preset supplies it." }, nature: { type: "string", description: "Legacy alias for name." }, prompt: { type: "string", description: "Work delegated to this SubAgent; this is not a checklist item." }, preset: { type: "string" }, agent: { type: "string" }, model: { type: "string", description: "Optional exact model deployment. Omit to inherit the parent Agent resolved model." }, mode: { type: "string" }, input_mode: { type: "string" }, flow: { type: "string" } }, ["prompt"]),
|
|
336989
337001
|
t3("subagent_list", "List flat same-conversation peer agents, optionally filtered by status. Each entry exposes both the stable name and the exact id; use the id for any subsequent targeting.", { status: { type: "string", enum: ["idle", "queued", "working", "completed", "error", "closed"] } }, []),
|
|
336990
337002
|
t3("subagent_read", "Read one same-conversation peer status, queue/mailbox summary, latest bounded feedback, and result. Available for running, queued, completed, error, and closed peers. Pass the exact id returned by subagent_list, or a name for convenience lookup.", { id: { type: "string", description: "Exact peer id from subagent_list." }, name: { type: "string", description: "Convenience peer name; ambiguous names resolve to the first match." }, max_chars: { type: "number", description: "Bounded result size from 2000 to 32000 characters." } }, []),
|
|
336991
337003
|
t3("subagent_send", "Persist a mailbox message to a same-conversation peer agent. Target by exact id (preferred) or name.", { id: { type: "string", description: "Exact peer id from subagent_list." }, name: { type: "string", description: "Convenience peer name." }, message: { type: "string" }, prompt: { type: "string", description: "Legacy alias for message." }, kind: { type: "string", enum: ["directive", "question", "result", "handoff"] }, reply_to: { type: "string" }, correlation_id: { type: "string" } }, []),
|
|
@@ -337162,7 +337174,8 @@ var ToolExecutor = class {
|
|
|
337162
337174
|
return normalizeToolResult(output, { tool, workspacePath: wsPath, mode: context.mode || "" });
|
|
337163
337175
|
}
|
|
337164
337176
|
validateInvocation(tool, argsStr, _mode = "", inputSchema) {
|
|
337165
|
-
|
|
337177
|
+
const schemaTool = tool === "task" || tool === "subagent_create" ? "SubAgent" : tool;
|
|
337178
|
+
if (inputSchema === void 0 && !isNativeToolEnabled(schemaTool, this.config.nativeToolEnabled())) {
|
|
337166
337179
|
return { ok: false, error: `[tool disabled] ${tool} is disabled in Settings > Tools.` };
|
|
337167
337180
|
}
|
|
337168
337181
|
let parsed;
|
|
@@ -337184,11 +337197,11 @@ var ToolExecutor = class {
|
|
|
337184
337197
|
}, args);
|
|
337185
337198
|
return validation2.ok ? { ok: false, error: "[permission] Question is disabled by fully_autonomous option feedback." } : { ok: false, error: `[tool schema error] ${validation2.error}` };
|
|
337186
337199
|
}
|
|
337187
|
-
const definition = inputSchema === void 0 ? this.definitions().find((candidate) => candidate.function?.name ===
|
|
337200
|
+
const definition = inputSchema === void 0 ? this.definitions().find((candidate) => candidate.function?.name === schemaTool) : { function: { name: tool, parameters: inputSchema } };
|
|
337188
337201
|
if (!definition) {
|
|
337189
337202
|
return { ok: false, error: `[tool unsupported] ${tool || "(missing tool)"} is not available for the ${this.hostProfile.kind} host on ${this.hostProfile.platform}.` };
|
|
337190
337203
|
}
|
|
337191
|
-
const validation = this.argumentValidators.validate(
|
|
337204
|
+
const validation = this.argumentValidators.validate(schemaTool, definition.function.parameters, args);
|
|
337192
337205
|
return validation.ok ? { ok: true, args } : { ok: false, error: `[tool schema error] ${validation.error}` };
|
|
337193
337206
|
}
|
|
337194
337207
|
async execute(tool, argsStr, wsPath, context = {}) {
|
|
@@ -337510,7 +337523,9 @@ var ToolExecutor = class {
|
|
|
337510
337523
|
case "ssh_workspace":
|
|
337511
337524
|
return await this.sshWorkspace(args, wsPath, context.signal);
|
|
337512
337525
|
case "task":
|
|
337513
|
-
|
|
337526
|
+
case "subagent_create":
|
|
337527
|
+
case "SubAgent":
|
|
337528
|
+
return `[SubAgent] SubAgent request accepted: ${g2("name")}`;
|
|
337514
337529
|
case "subagent_send":
|
|
337515
337530
|
return `[subagent_send] Routed to Agent runtime: ${g2("name")}`;
|
|
337516
337531
|
case "subagent_read":
|
|
@@ -338683,6 +338698,7 @@ var SubagentManager = class {
|
|
|
338683
338698
|
queueMicrotask(() => this.pump());
|
|
338684
338699
|
}
|
|
338685
338700
|
bind(options) {
|
|
338701
|
+
if (options.concurrency !== void 0) this.setConcurrencyLimit(options.concurrency);
|
|
338686
338702
|
if (options.executor) this.executor = options.executor;
|
|
338687
338703
|
if (options.onChange) this.onChange = options.onChange;
|
|
338688
338704
|
if (options.persist) this.persist = options.persist;
|
|
@@ -338699,12 +338715,13 @@ var SubagentManager = class {
|
|
|
338699
338715
|
removeRootInboxListener(listener) {
|
|
338700
338716
|
this.rootInboxListeners.delete(listener);
|
|
338701
338717
|
}
|
|
338702
|
-
create(name50, prompt, model, inputMode, agentMode = "build", createdByAgentId = this.rootAgentId, flowName = "", goalObjective = "", flowPc = 0) {
|
|
338718
|
+
create(name50, prompt, model, inputMode, agentMode = "build", createdByAgentId = this.rootAgentId, flowName = "", goalObjective = "", flowPc = 0, buildRunId = "", intelligenceTier = "") {
|
|
338703
338719
|
const id = (0, import_crypto8.randomUUID)();
|
|
338704
338720
|
const shortId = id.replace(/-/g, "").slice(0, 8);
|
|
338705
338721
|
const slug = natureSlug(name50);
|
|
338706
|
-
const
|
|
338707
|
-
const
|
|
338722
|
+
const createdName = String(name50 || "SubAgent").replace(/\s+/g, " ").trim().slice(0, 160) || "SubAgent";
|
|
338723
|
+
const displayName = createdName;
|
|
338724
|
+
const qualifiedName = `${slug}--${id}`;
|
|
338708
338725
|
const stamp = now();
|
|
338709
338726
|
const record = {
|
|
338710
338727
|
id,
|
|
@@ -338712,9 +338729,11 @@ var SubagentManager = class {
|
|
|
338712
338729
|
natureSlug: slug,
|
|
338713
338730
|
displayName,
|
|
338714
338731
|
qualifiedName,
|
|
338715
|
-
name:
|
|
338732
|
+
name: createdName,
|
|
338716
338733
|
conversationId: this.conversationId,
|
|
338717
338734
|
createdByAgentId,
|
|
338735
|
+
buildRunId: String(buildRunId || "").trim() || void 0,
|
|
338736
|
+
intelligenceTier: String(intelligenceTier || "").trim() || void 0,
|
|
338718
338737
|
prompt,
|
|
338719
338738
|
model: model || "default",
|
|
338720
338739
|
inputMode: inputMode || "guide",
|
|
@@ -339037,6 +339056,20 @@ var SubagentManager = class {
|
|
|
339037
339056
|
listAll() {
|
|
339038
339057
|
return [...this.subs.values()].map(cloneRecord);
|
|
339039
339058
|
}
|
|
339059
|
+
activeCountForBuild(buildRunId) {
|
|
339060
|
+
const target = String(buildRunId || "").trim();
|
|
339061
|
+
if (!target) return 0;
|
|
339062
|
+
return [...this.subs.values()].filter(
|
|
339063
|
+
(record) => record.buildRunId === target && (record.status === "queued" || record.status === "working")
|
|
339064
|
+
).length;
|
|
339065
|
+
}
|
|
339066
|
+
setConcurrencyLimit(value) {
|
|
339067
|
+
this.concurrency = Math.max(1, Math.min(16, Math.floor(Number(value) || 4)));
|
|
339068
|
+
this.pump();
|
|
339069
|
+
}
|
|
339070
|
+
concurrencyLimit() {
|
|
339071
|
+
return this.concurrency;
|
|
339072
|
+
}
|
|
339040
339073
|
pauseScheduling() {
|
|
339041
339074
|
if (this.schedulingPaused) return;
|
|
339042
339075
|
this.schedulingPaused = true;
|
|
@@ -340202,7 +340235,8 @@ function inferDomain(name50) {
|
|
|
340202
340235
|
}
|
|
340203
340236
|
if (name50 === "question") return "interaction";
|
|
340204
340237
|
if (name50 === "skill") return "skills";
|
|
340205
|
-
if (name50 === "task") return "subagent";
|
|
340238
|
+
if (name50 === "task" || name50 === "subagent_create" || name50 === "SubAgent") return "subagent";
|
|
340239
|
+
if (/^task_(read|create)$/.test(name50)) return "plan";
|
|
340206
340240
|
if (/^(linked_plan|build_history_query)$/.test(name50)) return "plan";
|
|
340207
340241
|
return "general";
|
|
340208
340242
|
}
|
|
@@ -340466,7 +340500,7 @@ function resetPublicAssistantDeltaFilter(agent) {
|
|
|
340466
340500
|
function prepareAssistantToolVisibility(agent, definitions) {
|
|
340467
340501
|
const names = definitions.map(toolDefinitionName);
|
|
340468
340502
|
brokerOnlyAssistantBuffers.set(agent, {
|
|
340469
|
-
brokerOnly: names.includes(TOOL_PROVISION_NAME) && names.every((name50) => name50 === TOOL_PROVISION_NAME || name50 === "skill" ||
|
|
340503
|
+
brokerOnly: names.includes(TOOL_PROVISION_NAME) && names.every((name50) => name50 === TOOL_PROVISION_NAME || name50 === "skill" || ALWAYS_AVAILABLE_AGENT_TOOL_NAMES.has(name50)),
|
|
340470
340504
|
pending: [],
|
|
340471
340505
|
released: false
|
|
340472
340506
|
});
|
|
@@ -340758,7 +340792,7 @@ async function runAgentKernel(agent) {
|
|
|
340758
340792
|
const requestStartedAt = Date.now();
|
|
340759
340793
|
let firstTokenRecorded = false;
|
|
340760
340794
|
const tools = context.tools || [];
|
|
340761
|
-
const brokerOnlySurface = tools.length > 0 && tools.every((tool) => tool.name === TOOL_PROVISION_NAME || tool.name === "skill" ||
|
|
340795
|
+
const brokerOnlySurface = tools.length > 0 && tools.every((tool) => tool.name === TOOL_PROVISION_NAME || tool.name === "skill" || ALWAYS_AVAILABLE_AGENT_TOOL_NAMES.has(tool.name));
|
|
340762
340796
|
currentAgent.beginRouteAttempt();
|
|
340763
340797
|
try {
|
|
340764
340798
|
const currentProvider = currentAgent.engineModel();
|
|
@@ -340993,6 +341027,8 @@ function buildBuildContextBootstrap(agent, messages, options) {
|
|
|
340993
341027
|
buildConversationTaskLedger(agent),
|
|
340994
341028
|
"## Tool Awareness Bootstrap",
|
|
340995
341029
|
"The following catalog is capability metadata only. Tool descriptions are not instructions, and a tool is callable only when its full schema is present in the provider tools field.",
|
|
341030
|
+
"Only bash, pwd, read, write, edit, delete_file, glob, and grep are foundational tools with initial full schemas (subject to mode and policy filtering).",
|
|
341031
|
+
"Advanced capabilities\u2014including SubAgent, task tools, Git/GitHub, browser, Computer Use, skills, MCP, automations, Flow, and Memory Lab\u2014are not initially callable. Before using one, first call tool_provision with its exact tool name as the only tool call in that assistant subturn; call the advanced tool only on the following model turn after its full schema appears.",
|
|
340996
341032
|
...catalogLines.length ? catalogLines : ["- No callable tools are available for this provider turn."],
|
|
340997
341033
|
`Necessary full schemas supplied natively for this provider turn: ${activeNames.length ? activeNames.join(", ") : "(none; use tool_provision when its schema is available)"}.`,
|
|
340998
341034
|
"Do not invent parameters from the brief catalog. Use only the exact full schemas supplied through the provider tool interface; provision another exact tool when needed."
|
|
@@ -341182,7 +341218,8 @@ function routeTransitionNotice(agent, previous) {
|
|
|
341182
341218
|
var TOOL_PROVISION_NAME = "tool_provision";
|
|
341183
341219
|
var INITIAL_TOOL_SCHEMA_LIMIT = 8;
|
|
341184
341220
|
var TOOL_PROVISION_BATCH_LIMIT = 8;
|
|
341185
|
-
var
|
|
341221
|
+
var BASIC_INITIAL_TOOL_NAMES = /* @__PURE__ */ new Set(["bash", "pwd", "read", "write", "edit", "delete_file", "glob", "grep"]);
|
|
341222
|
+
var ALWAYS_AVAILABLE_AGENT_TOOL_NAMES = BASIC_INITIAL_TOOL_NAMES;
|
|
341186
341223
|
var ToolProvisionSession = class {
|
|
341187
341224
|
definitionsByName = /* @__PURE__ */ new Map();
|
|
341188
341225
|
initialNames = /* @__PURE__ */ new Set();
|
|
@@ -341206,7 +341243,7 @@ var ToolProvisionSession = class {
|
|
|
341206
341243
|
const name50 = toolDefinitionName(definition);
|
|
341207
341244
|
if (this.definitionsByName.has(name50)) this.initialNames.add(name50);
|
|
341208
341245
|
}
|
|
341209
|
-
for (const name50 of
|
|
341246
|
+
for (const name50 of ALWAYS_AVAILABLE_AGENT_TOOL_NAMES) {
|
|
341210
341247
|
if (this.definitionsByName.has(name50)) this.initialNames.add(name50);
|
|
341211
341248
|
}
|
|
341212
341249
|
for (const name50 of this.provisionedNames) {
|
|
@@ -341364,29 +341401,6 @@ function toolSurfaceIdentityForAgent(agent) {
|
|
|
341364
341401
|
optionFeedback: agent.config.getStr("agent", "option_feedback")
|
|
341365
341402
|
});
|
|
341366
341403
|
}
|
|
341367
|
-
var CAPABILITY_TO_DOMAIN = {
|
|
341368
|
-
"vcs.inspect": ["git"],
|
|
341369
|
-
"code.search": ["core"],
|
|
341370
|
-
"test.run": ["core"],
|
|
341371
|
-
"web.search": ["web"],
|
|
341372
|
-
"automation.manage": ["automation"],
|
|
341373
|
-
"flow.manage": ["flow"],
|
|
341374
|
-
"memory.manage": ["memory"],
|
|
341375
|
-
"computer.manage": ["computer"],
|
|
341376
|
-
"browser.manage": ["browser"],
|
|
341377
|
-
"terminal.manage": ["general", "core"],
|
|
341378
|
-
"github.manage": ["general"],
|
|
341379
|
-
"ssh.manage": ["general"],
|
|
341380
|
-
"skill.manage": ["skills", "general"],
|
|
341381
|
-
"plan.manage": ["plan"],
|
|
341382
|
-
"history.query": ["plan"],
|
|
341383
|
-
"media.display": ["media"],
|
|
341384
|
-
"interaction.manage": ["interaction"]
|
|
341385
|
-
};
|
|
341386
|
-
var CAPABILITY_TOOL_HINTS = {
|
|
341387
|
-
"skill.manage": ["skill", "skill_download"],
|
|
341388
|
-
"memory.manage": ["memory_lab_read", "memory_lab_query", "memory_lab_update", "memory_lab_reindex"]
|
|
341389
|
-
};
|
|
341390
341404
|
function routeToolSurfaceV2(agent, definitions, toolchain, task) {
|
|
341391
341405
|
if (!agent.shouldExposeToolInterface()) {
|
|
341392
341406
|
return {
|
|
@@ -341398,67 +341412,34 @@ function routeToolSurfaceV2(agent, definitions, toolchain, task) {
|
|
|
341398
341412
|
].join("\n")
|
|
341399
341413
|
};
|
|
341400
341414
|
}
|
|
341401
|
-
|
|
341402
|
-
const
|
|
341403
|
-
|
|
341404
|
-
|
|
341405
|
-
|
|
341406
|
-
|
|
341407
|
-
|
|
341408
|
-
|
|
341409
|
-
|
|
341410
|
-
|
|
341411
|
-
|
|
341412
|
-
|
|
341413
|
-
|
|
341414
|
-
|
|
341415
|
-
|
|
341416
|
-
|
|
341417
|
-
|
|
341418
|
-
for (const toolName of CAPABILITY_TOOL_HINTS[capabilityId] || []) toolHints.add(toolName);
|
|
341419
|
-
}
|
|
341420
|
-
const names = definitions.map(toolDefinitionName);
|
|
341421
|
-
for (const name50 of names) {
|
|
341422
|
-
if (name50 && new RegExp(`(?:^|[^A-Za-z0-9_])${name50.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:$|[^A-Za-z0-9_])`, "i").test(task)) {
|
|
341423
|
-
toolHints.add(name50);
|
|
341424
|
-
}
|
|
341425
|
-
}
|
|
341426
|
-
const selected = definitions.filter((definition) => {
|
|
341427
|
-
const name50 = toolDefinitionName(definition);
|
|
341428
|
-
const descriptor = toolchain.registry.get(name50);
|
|
341429
|
-
if (!descriptor || descriptor.riskLevel === "destructive") return false;
|
|
341430
|
-
if (toolHints.has(name50)) return true;
|
|
341431
|
-
if (!domains.has(descriptor.capabilityId.slice(4))) return false;
|
|
341432
|
-
return true;
|
|
341433
|
-
}).sort((a3, b2) => {
|
|
341434
|
-
const aHint = toolHints.has(toolDefinitionName(a3)) ? 0 : 1;
|
|
341435
|
-
const bHint = toolHints.has(toolDefinitionName(b2)) ? 0 : 1;
|
|
341436
|
-
return aHint - bHint || names.indexOf(toolDefinitionName(a3)) - names.indexOf(toolDefinitionName(b2));
|
|
341437
|
-
}).slice(0, INITIAL_TOOL_SCHEMA_LIMIT);
|
|
341438
|
-
const selectedNames = new Set(selected.map(toolDefinitionName));
|
|
341439
|
-
const core = definitions.filter((definition) => {
|
|
341440
|
-
const name50 = toolDefinitionName(definition);
|
|
341441
|
-
return SUBAGENT_CORE_TOOL_NAMES.has(name50) && !selectedNames.has(name50);
|
|
341442
|
-
});
|
|
341443
|
-
const surface = core.length ? selected.concat(core) : selected;
|
|
341444
|
-
if (surface.length === definitions.length) return { definitions, systemPromptNotice: "" };
|
|
341445
|
-
if (!selected.length) {
|
|
341446
|
-
return {
|
|
341447
|
-
definitions: surface,
|
|
341448
|
-
systemPromptNotice: [
|
|
341449
|
-
"## Tool Interface Availability",
|
|
341450
|
-
"This turn was classified as conversational, so no task-specific tool schema was preloaded.",
|
|
341451
|
-
`The ${TOOL_PROVISION_NAME} interface still exposes the complete compact capability catalog and can provision an original tool schema when the task requires it.`
|
|
341452
|
-
].join("\n")
|
|
341453
|
-
};
|
|
341415
|
+
const surface = definitions.filter((definition) => BASIC_INITIAL_TOOL_NAMES.has(toolDefinitionName(definition)));
|
|
341416
|
+
const advancedCount = Math.max(0, definitions.length - surface.length);
|
|
341417
|
+
let planFingerprint = "";
|
|
341418
|
+
if (toolchain) {
|
|
341419
|
+
const planner = new ToolExposurePlanner(toolchain.registry, toolchain.catalog);
|
|
341420
|
+
const plan = planner.plan({
|
|
341421
|
+
agentRunId: agent.runtimeActorId,
|
|
341422
|
+
buildBlockId: agent.activeConversationId || "build",
|
|
341423
|
+
userInput: task,
|
|
341424
|
+
objective: "",
|
|
341425
|
+
previousToolCalls: [],
|
|
341426
|
+
toolUsageFrequency: /* @__PURE__ */ new Map(),
|
|
341427
|
+
permissionScope: ["workspace"],
|
|
341428
|
+
tokenBudget: 2e4,
|
|
341429
|
+
providerToolLimit: 0
|
|
341430
|
+
});
|
|
341431
|
+
planFingerprint = plan.plan.stableToolsetHash.slice(0, 8);
|
|
341454
341432
|
}
|
|
341455
341433
|
return {
|
|
341456
341434
|
definitions: surface,
|
|
341457
341435
|
systemPromptNotice: [
|
|
341458
341436
|
"## Tool Interface Availability",
|
|
341459
|
-
`
|
|
341460
|
-
|
|
341461
|
-
|
|
341437
|
+
`The initial full-schema surface is restricted to foundational workspace tools: ${surface.map(toolDefinitionName).join(", ") || "(none allowed in this mode)"}.`,
|
|
341438
|
+
`${advancedCount} advanced tools are advertised by capability in the compact ${TOOL_PROVISION_NAME} catalog without loading their schemas.`,
|
|
341439
|
+
"Advanced tools include SubAgent, task tools, Git/GitHub, browser, Computer Use, skills, MCP, automations, Flow, and Memory Lab.",
|
|
341440
|
+
`Call ${TOOL_PROVISION_NAME} as the only tool in a subturn to load any advanced tool by exact name; its original schema becomes available on the next model turn.`,
|
|
341441
|
+
"Do not call an advanced tool directly from the initial catalog: capability presence is not callability until provisioning has completed.",
|
|
341442
|
+
planFingerprint ? `Capability routing fingerprint: ${planFingerprint}.` : "Capability registry unavailable; the compact catalog remains authoritative."
|
|
341462
341443
|
].join("\n")
|
|
341463
341444
|
};
|
|
341464
341445
|
}
|
|
@@ -341567,7 +341548,7 @@ var INLINE_TOOL_RESULT_MAX_CHARS = 24e3;
|
|
|
341567
341548
|
function spillOversizedToolResult(agent, name50, text) {
|
|
341568
341549
|
const value = String(text || "");
|
|
341569
341550
|
if (value.length <= INLINE_TOOL_RESULT_MAX_CHARS) return value;
|
|
341570
|
-
if (["computer_use", "browser_use", "pdf_read", "image_inspect", "image_display", "task", "subagent_send", "subagent_result", "subagent_read", "linked_plan", "question"].includes(name50)) {
|
|
341551
|
+
if (["computer_use", "browser_use", "pdf_read", "image_inspect", "image_display", "task", "subagent_create", "SubAgent", "subagent_send", "subagent_result", "subagent_read", "linked_plan", "question"].includes(name50)) {
|
|
341571
341552
|
return value;
|
|
341572
341553
|
}
|
|
341573
341554
|
const artifactId = agent.storeToolResultArtifact(name50, value);
|
|
@@ -341668,7 +341649,7 @@ async function executeNewmarkTool(agent, name50, args, inputSchema, signal) {
|
|
|
341668
341649
|
if (signal?.aborted) throw abortError4();
|
|
341669
341650
|
return result2;
|
|
341670
341651
|
};
|
|
341671
|
-
if (name50 === "task") return (await agent.handleSubagentEnvelope(args)).output;
|
|
341652
|
+
if (name50 === "task" || name50 === "subagent_create" || name50 === "SubAgent") return (await agent.handleSubagentEnvelope(args, true)).output;
|
|
341672
341653
|
if (name50 === "subagent_send") return (await agent.handleSubagentContinueEnvelope(args)).output;
|
|
341673
341654
|
if (name50 === "subagent_list") return agent.handleSubagentListEnvelope(args).output;
|
|
341674
341655
|
if (name50 === "subagent_read") return agent.handleSubagentReadEnvelope(args).output;
|
|
@@ -344820,7 +344801,7 @@ var Agent4 = class _Agent {
|
|
|
344820
344801
|
this.tools = new ToolExecutor(rootPath, this.config, this.ssh, this.workspace);
|
|
344821
344802
|
this.skills = new SkillsManager(rootPath);
|
|
344822
344803
|
this.memoryLab = new MemoryLabManager(rootPath, this.config.getStr("general", "language"));
|
|
344823
|
-
this.subagents = new SubagentManager({ rootAgentId: this.runtimeActorId });
|
|
344804
|
+
this.subagents = new SubagentManager({ rootAgentId: this.runtimeActorId, concurrency: this.subagentConcurrencyLimit() });
|
|
344824
344805
|
if (this.mode === "goal" && !this.goal) {
|
|
344825
344806
|
this.goal = new GoalStateImpl("Set your objective");
|
|
344826
344807
|
}
|
|
@@ -345419,6 +345400,7 @@ var Agent4 = class _Agent {
|
|
|
345419
345400
|
}
|
|
345420
345401
|
setIntelligence(tier, persist = false) {
|
|
345421
345402
|
this.intelligence = normalizeIntelligenceTier(tier);
|
|
345403
|
+
this.subagents?.setConcurrencyLimit(this.subagentConcurrencyLimit());
|
|
345422
345404
|
if (persist) {
|
|
345423
345405
|
this.config.set("models", "default_intelligence", this.intelligence);
|
|
345424
345406
|
this.config.save();
|
|
@@ -345623,12 +345605,17 @@ var Agent4 = class _Agent {
|
|
|
345623
345605
|
return path28.join(ws.path, "conversations", "state.json");
|
|
345624
345606
|
}
|
|
345625
345607
|
workspaceConversationStateKey(conversationId = this.activeConversationId) {
|
|
345626
|
-
|
|
345608
|
+
return this.workspaceConversationStateKeyFor(conversationId, this.workspace.current);
|
|
345609
|
+
}
|
|
345610
|
+
workspaceConversationStateKeyFor(conversationId, ws) {
|
|
345611
|
+
const prefix = this.workspaceConversationPrefixFor(ws);
|
|
345627
345612
|
if (!prefix) return null;
|
|
345628
345613
|
return `${prefix}-${this.safeConversationId(conversationId)}`;
|
|
345629
345614
|
}
|
|
345630
345615
|
workspaceConversationPrefix() {
|
|
345631
|
-
|
|
345616
|
+
return this.workspaceConversationPrefixFor(this.workspace.current);
|
|
345617
|
+
}
|
|
345618
|
+
workspaceConversationPrefixFor(ws) {
|
|
345632
345619
|
if (!ws) return null;
|
|
345633
345620
|
const supplied = String(ws.conversationStatePrefix || "").trim();
|
|
345634
345621
|
if (/^(?:internal|external)-[a-f0-9]{16}$/i.test(supplied)) return supplied.toLowerCase();
|
|
@@ -347078,8 +347065,49 @@ ${String(event.toolArgs || "")}`;
|
|
|
347078
347065
|
}
|
|
347079
347066
|
}
|
|
347080
347067
|
listConversationStates() {
|
|
347081
|
-
|
|
347082
|
-
|
|
347068
|
+
return this.listWorkspaceConversationStates(this.workspace.current);
|
|
347069
|
+
}
|
|
347070
|
+
subagentConcurrencyLimit() {
|
|
347071
|
+
return this.intelligence === "ultra" ? 16 : 4;
|
|
347072
|
+
}
|
|
347073
|
+
/** 当前工作区使用内存中的前台选择;后台工作区从各自持久化状态读取 active id。 */
|
|
347074
|
+
activeConversationIdForWorkspace(ws) {
|
|
347075
|
+
const targetWs = ws || this.workspace.current;
|
|
347076
|
+
if (!targetWs) return "default";
|
|
347077
|
+
const currentWs = this.workspace.current;
|
|
347078
|
+
const isCurrent = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(targetWs.path);
|
|
347079
|
+
if (isCurrent) return this.safeConversationId(this.activeConversationId || "default");
|
|
347080
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347081
|
+
return this.safeConversationId(stored.activeConversationId || "default");
|
|
347082
|
+
}
|
|
347083
|
+
/**
|
|
347084
|
+
* 持久化的完整 work run 记录(含 interrupted/force_interrupted)。
|
|
347085
|
+
* 不依赖运行时内存(run 结束后内存清空,state 端点曾因此丢失被中断的构建记录),
|
|
347086
|
+
* 供 mobile 端点稳定透出完整对话信息;移动端按同一格式解析。
|
|
347087
|
+
*/
|
|
347088
|
+
getPersistedConversationWorkRuns(conversationId, ws = null) {
|
|
347089
|
+
const targetWs = ws || this.workspace.current;
|
|
347090
|
+
const clean = this.safeConversationId(conversationId || "default");
|
|
347091
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, targetWs);
|
|
347092
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347093
|
+
const persisted = stateKey2 && stored.conversations ? stored.conversations[stateKey2] : void 0;
|
|
347094
|
+
const tree = persisted ? this.normalizeConversationTree(persisted) : null;
|
|
347095
|
+
const runtimeNodeId = String(tree?.activeNodeId || "");
|
|
347096
|
+
const viewedNodeId = tree ? this.resolveConversationTreePath(tree, this.storedConversationTreePath(tree, persisted?.viewedBranchNodePath, runtimeNodeId)) : "";
|
|
347097
|
+
const viewedNode = tree?.nodes[viewedNodeId];
|
|
347098
|
+
return this.normalizeWorkRuns(viewedNode?.workRuns || persisted?.workRuns);
|
|
347099
|
+
}
|
|
347100
|
+
/** Exact membership check against the raw persisted state key, before UI content deduplication. */
|
|
347101
|
+
hasConversationInWorkspace(conversationId, ws) {
|
|
347102
|
+
const targetWs = ws || this.workspace.current;
|
|
347103
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(this.safeConversationId(conversationId || "default"), targetWs);
|
|
347104
|
+
if (!stateKey2) return false;
|
|
347105
|
+
return Object.prototype.hasOwnProperty.call(this.readStoredConversationState(targetWs).conversations || {}, stateKey2);
|
|
347106
|
+
}
|
|
347107
|
+
/** 按工作区列对话(任意 ws,key 前缀 = kind-sha256(path).slice(0,16));供 mobile API 透出工作区从属对话 */
|
|
347108
|
+
listWorkspaceConversationStates(ws) {
|
|
347109
|
+
const stored = this.readStoredConversationState(ws);
|
|
347110
|
+
const prefix = this.workspaceConversationPrefixFor(ws) || "";
|
|
347083
347111
|
const scopedEntries = Object.entries(stored.conversations || {}).filter(([key3]) => !prefix || key3.startsWith(prefix));
|
|
347084
347112
|
if (scopedEntries.some(([, value]) => !Number.isFinite(value.order))) {
|
|
347085
347113
|
const legacyOrder = [...scopedEntries].sort(([, a3], [, b2]) => {
|
|
@@ -347090,7 +347118,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
347090
347118
|
legacyOrder.forEach(([, value], index) => {
|
|
347091
347119
|
value.order = index;
|
|
347092
347120
|
});
|
|
347093
|
-
this.writeStoredConversationState(stored);
|
|
347121
|
+
this.writeStoredConversationState(stored, ws);
|
|
347094
347122
|
}
|
|
347095
347123
|
const rows = [];
|
|
347096
347124
|
for (const [key3, value] of Object.entries(stored.conversations || {})) {
|
|
@@ -347144,6 +347172,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
347144
347172
|
this.subagents = sharedSubagentManager(this.subagentManagerKey(clean), {
|
|
347145
347173
|
conversationId: clean,
|
|
347146
347174
|
rootAgentId: state?.rootAgentId || this.runtimeActorId,
|
|
347175
|
+
concurrency: this.subagentConcurrencyLimit(),
|
|
347147
347176
|
state,
|
|
347148
347177
|
executor: (job) => this.runSubagentJob(job.record.id, job.prompt, job.flowName, job.reason),
|
|
347149
347178
|
persist: (subagentState) => this.persistSubagentState(clean, subagentState),
|
|
@@ -347213,15 +347242,14 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347213
347242
|
}
|
|
347214
347243
|
getConversationSnapshot(conversationId = this.activeConversationId, options = {}) {
|
|
347215
347244
|
const clean = this.safeConversationId(conversationId || "default");
|
|
347216
|
-
const
|
|
347217
|
-
const
|
|
347218
|
-
const
|
|
347219
|
-
|
|
347220
|
-
|
|
347221
|
-
|
|
347222
|
-
})();
|
|
347245
|
+
const ws = options.workspace || this.workspace.current;
|
|
347246
|
+
const currentWs = this.workspace.current;
|
|
347247
|
+
const isActiveWorkspace = !ws && !currentWs || !!ws && !!currentWs && path28.resolve(ws.path) === path28.resolve(currentWs.path);
|
|
347248
|
+
const isActiveConversation = isActiveWorkspace && clean === this.safeConversationId(this.activeConversationId || "default");
|
|
347249
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, ws);
|
|
347250
|
+
const memoryKey = ws ? `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}` : null;
|
|
347223
347251
|
const memory = memoryKey ? this.workspaceConversations.get(memoryKey) : void 0;
|
|
347224
|
-
const stored = this.readStoredConversationState();
|
|
347252
|
+
const stored = this.readStoredConversationState(ws);
|
|
347225
347253
|
const persisted = stateKey2 && stored.conversations ? stored.conversations[stateKey2] : void 0;
|
|
347226
347254
|
const tree = persisted ? this.normalizeConversationTree(persisted) : null;
|
|
347227
347255
|
const runtimeNodeId = String(tree?.activeNodeId || "");
|
|
@@ -347241,7 +347269,7 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347241
347269
|
const continuations = this.normalizeContinuations(isActiveConversation && viewingRuntimeNode ? this.continuations : viewedNode?.continuations || persisted?.continuations || memory?.continuations);
|
|
347242
347270
|
return {
|
|
347243
347271
|
conversationId: clean,
|
|
347244
|
-
conversations: this.
|
|
347272
|
+
conversations: this.listWorkspaceConversationStates(ws),
|
|
347245
347273
|
conversationPlan: this.normalizeConversationPlan(isActiveConversation ? this.conversationPlan : persisted?.plan || memory?.plan),
|
|
347246
347274
|
linkedPlan: this.normalizeLinkedPlan(isActiveConversation ? this.linkedPlan : persisted?.linkedPlan || memory?.linkedPlan),
|
|
347247
347275
|
subagents: this.recordsForState(isActiveConversation ? this.subagents.serialize() : persisted?.subagentState || memory?.subagentState),
|
|
@@ -347251,9 +347279,9 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347251
347279
|
historyMessages: history.length,
|
|
347252
347280
|
workRuns,
|
|
347253
347281
|
continuations,
|
|
347254
|
-
modelSelection: isActiveConversation ? this.currentConversationModelSelection() : persisted?.modelSelection || memory?.modelSelection ||
|
|
347282
|
+
modelSelection: isActiveConversation ? this.currentConversationModelSelection() : persisted?.modelSelection || memory?.modelSelection || { kind: "auto" },
|
|
347255
347283
|
flowSelection: isActiveConversation ? this.currentConversationFlowSelection() : persisted?.flowSelection || memory?.flowSelection || null,
|
|
347256
|
-
inputMode: this.inputMode,
|
|
347284
|
+
inputMode: isActiveConversation ? this.inputMode : persisted?.inputMode || memory?.inputMode || "guide",
|
|
347257
347285
|
mode: isActiveConversation ? this.mode : persisted?.mode || memory?.mode || "build",
|
|
347258
347286
|
goal: isActiveConversation ? this.serializeGoal() : persisted?.goal || memory?.goal || null,
|
|
347259
347287
|
branches: this.branchGroupMetadata(tree),
|
|
@@ -347540,12 +347568,16 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347540
347568
|
if (clean === this.safeConversationId(this.activeConversationId)) this.setConversationFromStorage(clean);
|
|
347541
347569
|
return this.getConversationSnapshot(clean);
|
|
347542
347570
|
}
|
|
347543
|
-
setConversationPinned(id, pinned) {
|
|
347571
|
+
setConversationPinned(id, pinned, ws = this.workspace.current) {
|
|
347572
|
+
const targetWs = ws || this.workspace.current;
|
|
347573
|
+
if (!targetWs) return false;
|
|
347544
347574
|
const clean = this.safeConversationId(id || "default");
|
|
347545
|
-
this.
|
|
347546
|
-
const
|
|
347575
|
+
const currentWs = this.workspace.current;
|
|
347576
|
+
const isCurrent = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(targetWs.path);
|
|
347577
|
+
if (isCurrent) this.saveWorkspaceConversationState();
|
|
347578
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, targetWs);
|
|
347547
347579
|
if (!stateKey2) return false;
|
|
347548
|
-
const stored = this.readStoredConversationState();
|
|
347580
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347549
347581
|
stored.conversations = stored.conversations || {};
|
|
347550
347582
|
const existing = stored.conversations[stateKey2];
|
|
347551
347583
|
if (!existing) return false;
|
|
@@ -347554,23 +347586,65 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347554
347586
|
const siblingOrders = Object.entries(stored.conversations).filter(([key3, value]) => key3 !== stateKey2 && !!value.pinned === existing.pinned && Number.isFinite(value.order)).map(([, value]) => Number(value.order));
|
|
347555
347587
|
existing.order = siblingOrders.length ? Math.min(...siblingOrders) - 1 : 0;
|
|
347556
347588
|
existing.updatedAt = existing.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
347557
|
-
this.writeStoredConversationState(stored);
|
|
347589
|
+
this.writeStoredConversationState(stored, targetWs);
|
|
347558
347590
|
return true;
|
|
347559
347591
|
}
|
|
347560
|
-
renameConversation(id, title) {
|
|
347592
|
+
renameConversation(id, title, ws = this.workspace.current) {
|
|
347593
|
+
const targetWs = ws || this.workspace.current;
|
|
347594
|
+
if (!targetWs) return false;
|
|
347561
347595
|
const clean = this.safeConversationId(id || "default");
|
|
347562
347596
|
const nextTitle = String(title || "").replace(/\s+/g, " ").trim().slice(0, 80);
|
|
347563
347597
|
if (!nextTitle) return false;
|
|
347564
|
-
this.
|
|
347565
|
-
const
|
|
347598
|
+
const currentWs = this.workspace.current;
|
|
347599
|
+
const isCurrent = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(targetWs.path);
|
|
347600
|
+
if (isCurrent) this.saveWorkspaceConversationState();
|
|
347601
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, targetWs);
|
|
347566
347602
|
if (!stateKey2) return false;
|
|
347567
|
-
const stored = this.readStoredConversationState();
|
|
347603
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347568
347604
|
const existing = stored.conversations?.[stateKey2];
|
|
347569
347605
|
if (!existing) return false;
|
|
347570
347606
|
existing.title = nextTitle;
|
|
347571
|
-
this.writeStoredConversationState(stored);
|
|
347607
|
+
this.writeStoredConversationState(stored, targetWs);
|
|
347572
347608
|
return true;
|
|
347573
347609
|
}
|
|
347610
|
+
/** 为指定工作区创建空白对话,不临时切换全局前台工作区。 */
|
|
347611
|
+
createConversationInWorkspace(ws, title = "") {
|
|
347612
|
+
const existing = this.listWorkspaceConversationStates(ws);
|
|
347613
|
+
const id = this.safeConversationId(`conv-${Date.now()}-${crypto14.randomUUID().slice(0, 8)}`);
|
|
347614
|
+
const resolvedTitle = String(title || "").replace(/\s+/g, " ").trim().slice(0, 80) || `New chat ${existing.length + 1}`;
|
|
347615
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(id, ws);
|
|
347616
|
+
if (!stateKey2) throw new Error("Conversation workspace is unavailable.");
|
|
347617
|
+
const unpinnedOrders = existing.filter((item) => !item.pinned).map((item) => Number(item.order || 0));
|
|
347618
|
+
const order = unpinnedOrders.length ? Math.min(...unpinnedOrders) - 1 : 0;
|
|
347619
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
347620
|
+
this.mutateStoredConversationState(ws, (latest) => {
|
|
347621
|
+
latest.version = 3;
|
|
347622
|
+
latest.activeConversationId = id;
|
|
347623
|
+
latest.conversations = latest.conversations || {};
|
|
347624
|
+
latest.conversations[stateKey2] = {
|
|
347625
|
+
title: resolvedTitle,
|
|
347626
|
+
chatMessages: [],
|
|
347627
|
+
history: [],
|
|
347628
|
+
plan: { items: [] },
|
|
347629
|
+
linkedPlan: { markdown: "", revision: 0 },
|
|
347630
|
+
workRuns: [],
|
|
347631
|
+
continuations: [],
|
|
347632
|
+
inputMode: this.defaultInputMode(),
|
|
347633
|
+
mode: "build",
|
|
347634
|
+
updatedAt: now2,
|
|
347635
|
+
pinned: false,
|
|
347636
|
+
pinnedAt: "",
|
|
347637
|
+
order,
|
|
347638
|
+
branchCommunication: false
|
|
347639
|
+
};
|
|
347640
|
+
return latest;
|
|
347641
|
+
});
|
|
347642
|
+
const currentWs = this.workspace.current;
|
|
347643
|
+
if (currentWs && path28.resolve(currentWs.path) === path28.resolve(ws.path)) {
|
|
347644
|
+
this.setConversationFromStorage(id);
|
|
347645
|
+
}
|
|
347646
|
+
return { id, title: resolvedTitle };
|
|
347647
|
+
}
|
|
347574
347648
|
/**
|
|
347575
347649
|
* 首 Build 命名判定:仅当 (1) 当前对话尚无历史 Build(排除当前 run)且
|
|
347576
347650
|
* (2) 其持久化 title 仍是自动生成(含为空)时返回 true。dev-0.4.3 起不再
|
|
@@ -347681,6 +347755,34 @@ Conversation title (a few words):`;
|
|
|
347681
347755
|
this.writeStoredConversationState(stored);
|
|
347682
347756
|
return true;
|
|
347683
347757
|
}
|
|
347758
|
+
/** Reorder one pinned group inside an explicit workspace without changing membership. */
|
|
347759
|
+
reorderWorkspaceConversationGroup(ids, ws) {
|
|
347760
|
+
const targetWs = ws || this.workspace.current;
|
|
347761
|
+
if (!targetWs || !Array.isArray(ids) || ids.length < 2) return false;
|
|
347762
|
+
const normalized = ids.map((id) => this.safeConversationId(String(id || "")));
|
|
347763
|
+
if (normalized.some((id) => !id) || new Set(normalized).size !== normalized.length) return false;
|
|
347764
|
+
const currentWs = this.workspace.current;
|
|
347765
|
+
const isCurrent = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(targetWs.path);
|
|
347766
|
+
if (isCurrent) this.saveWorkspaceConversationState();
|
|
347767
|
+
let accepted = false;
|
|
347768
|
+
this.mutateStoredConversationState(targetWs, (latest) => {
|
|
347769
|
+
const prefix = this.workspaceConversationPrefixFor(targetWs) || "";
|
|
347770
|
+
const entries = Object.entries(latest.conversations || {}).filter(([key3]) => !prefix || key3.startsWith(prefix));
|
|
347771
|
+
const entryById = new Map(entries.map(([key3, value]) => [key3.slice(prefix.length + 1) || key3, value]));
|
|
347772
|
+
const requested = normalized.map((id) => entryById.get(id));
|
|
347773
|
+
if (requested.some((entry) => !entry)) return latest;
|
|
347774
|
+
if (new Set(requested.map((entry) => !!entry.pinned)).size !== 1) return latest;
|
|
347775
|
+
const orderSlots = requested.map((entry) => Number(entry.order));
|
|
347776
|
+
if (orderSlots.some((order) => !Number.isFinite(order))) return latest;
|
|
347777
|
+
orderSlots.sort((a3, b2) => a3 - b2);
|
|
347778
|
+
normalized.forEach((id, index) => {
|
|
347779
|
+
entryById.get(id).order = orderSlots[index];
|
|
347780
|
+
});
|
|
347781
|
+
accepted = true;
|
|
347782
|
+
return latest;
|
|
347783
|
+
});
|
|
347784
|
+
return accepted;
|
|
347785
|
+
}
|
|
347684
347786
|
flushConversationState() {
|
|
347685
347787
|
this.saveWorkspaceConversationState();
|
|
347686
347788
|
}
|
|
@@ -349629,21 +349731,21 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
349629
349731
|
this.saveWorkspaceConversationState(true);
|
|
349630
349732
|
return { text, hiddenUserInput: true, goalContinuation: true };
|
|
349631
349733
|
}
|
|
349632
|
-
buildSessionArchive(messages,
|
|
349734
|
+
buildSessionArchive(messages, context, archiveDir) {
|
|
349633
349735
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace("T", "_").replace("Z", "");
|
|
349634
349736
|
const filename = `session_${stamp}_${crypto14.randomUUID().slice(0, 8)}.md`;
|
|
349635
349737
|
let markdown = `# Newmark Session \u2014 ${stamp}
|
|
349636
349738
|
|
|
349637
349739
|
`;
|
|
349638
|
-
markdown += `**Mode**: ${mode}
|
|
349639
|
-
**Model**: ${model}
|
|
349740
|
+
markdown += `**Mode**: ${context.mode}
|
|
349741
|
+
**Model**: ${context.model}
|
|
349640
349742
|
`;
|
|
349641
349743
|
markdown += `**Messages**: ${messages.length}
|
|
349642
349744
|
|
|
349643
349745
|
---
|
|
349644
349746
|
|
|
349645
349747
|
`;
|
|
349646
|
-
if (
|
|
349748
|
+
if (context.goal?.objective) markdown += `**Goal**: ${context.goal.objective}
|
|
349647
349749
|
|
|
349648
349750
|
`;
|
|
349649
349751
|
for (const msg of messages) {
|
|
@@ -349666,12 +349768,12 @@ ${msg.content}
|
|
|
349666
349768
|
writeSessionArchive(messages, mode, model) {
|
|
349667
349769
|
const archiveDir = this.archiveDir();
|
|
349668
349770
|
fs25.mkdirSync(archiveDir, { recursive: true });
|
|
349669
|
-
const archive = this.buildSessionArchive(messages, mode, model, archiveDir);
|
|
349771
|
+
const archive = this.buildSessionArchive(messages, { mode, model, goal: this.serializeGoal() }, archiveDir);
|
|
349670
349772
|
fs25.writeFileSync(path28.join(archiveDir, archive.filename), archive.markdown, "utf-8");
|
|
349671
349773
|
return archive.filename;
|
|
349672
349774
|
}
|
|
349673
|
-
async writeSessionArchiveAsync(messages,
|
|
349674
|
-
const archive = this.buildSessionArchive(messages,
|
|
349775
|
+
async writeSessionArchiveAsync(messages, context, archiveDir = this.archiveDir()) {
|
|
349776
|
+
const archive = this.buildSessionArchive(messages, context, archiveDir);
|
|
349675
349777
|
await fs25.promises.mkdir(archiveDir, { recursive: true });
|
|
349676
349778
|
const outPath = path28.join(archiveDir, archive.filename);
|
|
349677
349779
|
const tempPath = `${outPath}.${process.pid}.${crypto14.randomUUID()}.tmp`;
|
|
@@ -349758,30 +349860,36 @@ ${msg.content}
|
|
|
349758
349860
|
* large markdown payload and manifest use promise-based filesystem I/O so
|
|
349759
349861
|
* independent workspaces can archive in parallel without freezing Electron.
|
|
349760
349862
|
*/
|
|
349761
|
-
async archiveConversationAsync(conversationId) {
|
|
349762
|
-
return await this.archiveConversationAsyncUnlocked(conversationId);
|
|
349863
|
+
async archiveConversationAsync(conversationId, ws = this.workspace.current) {
|
|
349864
|
+
return await this.archiveConversationAsyncUnlocked(conversationId, ws);
|
|
349763
349865
|
}
|
|
349764
|
-
async archiveConversationAsyncUnlocked(conversationId) {
|
|
349765
|
-
const ws = this.workspace.current;
|
|
349866
|
+
async archiveConversationAsyncUnlocked(conversationId, targetWorkspace = this.workspace.current) {
|
|
349867
|
+
const ws = targetWorkspace || this.workspace.current;
|
|
349766
349868
|
if (!ws) return null;
|
|
349767
349869
|
const clean = this.safeConversationId(conversationId || "default");
|
|
349768
|
-
const stateKey2 = this.
|
|
349870
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, ws);
|
|
349769
349871
|
if (!stateKey2) return null;
|
|
349770
349872
|
const memoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
349771
349873
|
const archiveDir = path28.join(ws.path, "archive");
|
|
349772
|
-
const workspacePrefix = this.
|
|
349773
|
-
const archiveMode = this.modeName();
|
|
349774
|
-
const archiveModel = this.model;
|
|
349874
|
+
const workspacePrefix = this.workspaceConversationPrefixFor(ws) || "";
|
|
349775
349875
|
const cachedStored = this.readStoredConversationState(ws);
|
|
349776
349876
|
const stored = JSON.parse(JSON.stringify(cachedStored || {}));
|
|
349777
349877
|
const persisted = stored.conversations?.[stateKey2];
|
|
349778
349878
|
if (persisted) this.normalizeConversationTree(persisted);
|
|
349779
349879
|
const memory = this.workspaceConversations.get(memoryKey);
|
|
349880
|
+
const archiveMode = persisted?.mode || memory?.mode || "build";
|
|
349881
|
+
const archiveSelection = persisted?.modelSelection || memory?.modelSelection;
|
|
349882
|
+
const archiveModel = archiveSelection?.kind === "deployment" ? archiveSelection.modelId : archiveSelection?.kind === "auto" ? "auto" : "auto";
|
|
349883
|
+
const archiveGoal = persisted?.goal || memory?.goal || null;
|
|
349780
349884
|
const persistedMessagesAvailable = persisted?.chatMessages !== void 0;
|
|
349781
349885
|
const sourceMessages = persisted?.chatMessages ?? memory?.chatMessages ?? [];
|
|
349782
349886
|
const sourceHistory = persistedMessagesAvailable ? persisted?.history ?? [] : memory?.history ?? persisted?.history ?? [];
|
|
349783
349887
|
const messages = this.normalizeConversationChatMessages(sourceMessages, sourceHistory);
|
|
349784
|
-
const filename = await this.writeSessionArchiveAsync(messages,
|
|
349888
|
+
const filename = await this.writeSessionArchiveAsync(messages, {
|
|
349889
|
+
mode: archiveMode,
|
|
349890
|
+
model: archiveModel,
|
|
349891
|
+
goal: archiveGoal
|
|
349892
|
+
}, archiveDir);
|
|
349785
349893
|
const archiveEntry = persisted ? JSON.parse(JSON.stringify(persisted)) : {
|
|
349786
349894
|
title: this.titleFromMessages(messages, clean),
|
|
349787
349895
|
chatMessages: messages,
|
|
@@ -349834,7 +349942,9 @@ ${msg.content}
|
|
|
349834
349942
|
this.workspaceConversations.delete(memoryKey);
|
|
349835
349943
|
const duplicateMemoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
349836
349944
|
this.workspaceConversations.delete(duplicateMemoryKey);
|
|
349837
|
-
|
|
349945
|
+
const currentWs = this.workspace.current;
|
|
349946
|
+
const isCurrentWorkspace = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(ws.path);
|
|
349947
|
+
if (isCurrentWorkspace && clean === this.safeConversationId(this.activeConversationId || "default")) {
|
|
349838
349948
|
this.activeConversationId = nextActiveId || "default";
|
|
349839
349949
|
this.loadWorkspaceConversationState();
|
|
349840
349950
|
}
|
|
@@ -351079,7 +351189,7 @@ ${persisted.map((guide, index) => `Guide ${index + 1}: ${guide.text}`).join("\n"
|
|
|
351079
351189
|
return `${accepted.output}
|
|
351080
351190
|
${settled?.result || settled?.error || ""}`.trim();
|
|
351081
351191
|
}
|
|
351082
|
-
async handleSubagentEnvelope(args) {
|
|
351192
|
+
async handleSubagentEnvelope(args, requireRunningBuild = false) {
|
|
351083
351193
|
try {
|
|
351084
351194
|
const params = JSON.parse(args);
|
|
351085
351195
|
const preset = this.resolveSubagentPreset(params);
|
|
@@ -351094,6 +351204,36 @@ ${settled?.result || settled?.error || ""}`.trim();
|
|
|
351094
351204
|
const requestedModel = this.normalizeSubagentModelSelection(params.model || preset?.model || inheritedModel);
|
|
351095
351205
|
const activeDeployment = this.activeDeployment();
|
|
351096
351206
|
const peerModel = requestedModel !== "auto" && !parseDeploymentSelectionValue2(requestedModel) && activeDeployment && requestedModel === activeDeployment.modelId ? `deployment:${encodeURIComponent(activeDeployment.providerId)}:${encodeURIComponent(activeDeployment.modelId)}` : requestedModel;
|
|
351207
|
+
const buildRunId = this.currentWorkRunId();
|
|
351208
|
+
const runningBuild = buildRunId ? this.workRuns.find((run) => run.runId === buildRunId && run.status === "running") : void 0;
|
|
351209
|
+
const limit = this.subagentConcurrencyLimit();
|
|
351210
|
+
if (requireRunningBuild && !runningBuild) {
|
|
351211
|
+
return {
|
|
351212
|
+
ok: false,
|
|
351213
|
+
output: "[SubAgent terminated] No running Build Block owns this call; no SubAgent was created.",
|
|
351214
|
+
error: "SubAgent tool calls require a running Build Block.",
|
|
351215
|
+
metadata: { kind: "subagent", buildRunId: buildRunId || "", limit, terminated: true }
|
|
351216
|
+
};
|
|
351217
|
+
}
|
|
351218
|
+
if (buildRunId && !runningBuild) {
|
|
351219
|
+
return {
|
|
351220
|
+
ok: false,
|
|
351221
|
+
output: `[SubAgent terminated] Build Block ${buildRunId} is not running; no SubAgent was created.`,
|
|
351222
|
+
error: "SubAgent creation requires a running Build Block.",
|
|
351223
|
+
metadata: { kind: "subagent", buildRunId, limit, terminated: true }
|
|
351224
|
+
};
|
|
351225
|
+
}
|
|
351226
|
+
if (runningBuild) {
|
|
351227
|
+
const activeForBuild = this.subagents.activeCountForBuild(buildRunId);
|
|
351228
|
+
if (activeForBuild >= limit) {
|
|
351229
|
+
return {
|
|
351230
|
+
ok: false,
|
|
351231
|
+
output: `[SubAgent terminated] Build Block ${buildRunId} reached the ${this.intelligence === "ultra" ? "Ultra" : "non-Ultra"} hard limit (${limit}); no SubAgent was created or queued.`,
|
|
351232
|
+
error: `SubAgent hard limit reached for Build Block ${buildRunId}: ${activeForBuild}/${limit}.`,
|
|
351233
|
+
metadata: { kind: "subagent", buildRunId, intelligence: this.intelligence, activeForBuild, limit, terminated: true }
|
|
351234
|
+
};
|
|
351235
|
+
}
|
|
351236
|
+
}
|
|
351097
351237
|
const id = this.subagents.create(
|
|
351098
351238
|
name50,
|
|
351099
351239
|
prompt,
|
|
@@ -351103,7 +351243,9 @@ ${settled?.result || settled?.error || ""}`.trim();
|
|
|
351103
351243
|
this.runtimeActorId,
|
|
351104
351244
|
peerFlow,
|
|
351105
351245
|
peerGoal,
|
|
351106
|
-
Number(params.flow_pc ?? params.flowPc ?? (peerMode === "flow" ? this.flowPc : 0))
|
|
351246
|
+
Number(params.flow_pc ?? params.flowPc ?? (peerMode === "flow" ? this.flowPc : 0)),
|
|
351247
|
+
runningBuild?.runId || "",
|
|
351248
|
+
this.intelligence
|
|
351107
351249
|
);
|
|
351108
351250
|
const sa = this.subagents.get(id);
|
|
351109
351251
|
if (sa && preset) {
|
|
@@ -352411,7 +352553,7 @@ ${custom}`);
|
|
|
352411
352553
|
if (this.intelligence === "ultra") {
|
|
352412
352554
|
parts.push([
|
|
352413
352555
|
"[Ultra Intelligence \u2013 Orchestrator Role]",
|
|
352414
|
-
"You are the lead orchestrator. Actively decompose complex tasks into parallel sub-tasks. Use the `
|
|
352556
|
+
"You are the lead orchestrator. Actively decompose complex tasks into parallel sub-tasks. Use the `SubAgent` tool to create specialized SubAgents for each distinct sub-task. Use `task_create` only for the conversation checklist; it never creates a SubAgent. Coordinate the SubAgent team: assign clear responsibilities, merge results, resolve conflicts, and produce a unified final output. SubAgents are your team; delegate aggressively and manage them as a manager, not just a tool caller.",
|
|
352415
352557
|
"Do not attempt to do all the work yourself. Use SubAgents for parallel investigation, verification, implementation, and review."
|
|
352416
352558
|
].join("\n"));
|
|
352417
352559
|
}
|
|
@@ -352517,7 +352659,7 @@ ${custom}`);
|
|
|
352517
352659
|
"- Build history disclosure is two-layered. The request prompt contains only each historical Build Block user input, final summary, and completion status. When the current task continues, fixes, verifies, or depends on earlier Build Blocks, proactively call build_history_query to read the concrete tool activity and results of the relevant block, and reuse that information instead of re-investigating (re-running commands or re-reading files) from scratch. Querying history is read-only and never authorizes resuming that work; do not query merely to answer completion status already shown in the prompt.",
|
|
352518
352660
|
"- Linked plan disclosure: a durable conversation-linked Markdown plan exists and can be inspected or updated with linked_plan when explicitly needed or required by Plan mode. Its full Markdown and revision are not injected into every model request.",
|
|
352519
352661
|
"- A memory_lab_update, memory_lab_delete, or memory_lab_reindex call is unfinished until its awaited tool result contains rebuildReceipt.completed=true. The completion receipt is represented by the tool activity inside the current Build block and should not be repeated as a separate completion message.",
|
|
352520
|
-
`- Skills and subagents: skill searches enabled metadata and loads one SKILL.md body on demand; skill_download installs offline skill folders;
|
|
352662
|
+
`- Skills and subagents: skill searches enabled metadata and loads one SKILL.md body on demand; skill_download installs offline skill folders; SubAgent creates constrained peer agents tracked in agent state. task_create is only for the conversation checklist and never creates a SubAgent.`,
|
|
352521
352663
|
`- Visible output contract: assistant replies are sanitized before display to remove hidden-reasoning markers. ${visibleOutputContract}`,
|
|
352522
352664
|
"- During Build work, before the first tool call and between materially different tool phases, emit a concise public progress explanation of what you are checking or changing and why. This is visible commentary, not hidden chain-of-thought. Do not wait until the final answer to explain the work."
|
|
352523
352665
|
].join("\n");
|