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
|
@@ -327563,7 +327563,7 @@ var NATIVE_TOOL_CATALOG = [
|
|
|
327563
327563
|
{ 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" },
|
|
327564
327564
|
{ name: "terminal_takeover", label: "Terminal takeover", description: "Maintain a persistent Agent-controlled shell session.", category: "desktop", defaultEnabled: true },
|
|
327565
327565
|
{ name: "ssh_workspace", label: "OpenSSH workspace", description: "Manage native OpenSSH connections and link remote workspaces by PC_Hash.", category: "ssh", defaultEnabled: true },
|
|
327566
|
-
{ name: "
|
|
327566
|
+
{ name: "SubAgent", label: "SubAgent", description: "Create one real same-conversation peer SubAgent.", category: "agent", defaultEnabled: true, protected: true, availability: "mode-scoped" },
|
|
327567
327567
|
{ name: "subagent_list", label: "Subagent list", description: "List same-conversation peer agents.", category: "agent", defaultEnabled: true, protected: true, availability: "mode-scoped" },
|
|
327568
327568
|
{ 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" },
|
|
327569
327569
|
{ name: "subagent_send", label: "Subagent send", description: "Persist a message to a peer mailbox.", category: "agent", defaultEnabled: true, protected: true, availability: "mode-scoped" },
|
|
@@ -328315,7 +328315,7 @@ function defaultConfig() {
|
|
|
328315
328315
|
auto_archive_on_close: { _description: "Auto archive on close", _type: "boolean", value: true }
|
|
328316
328316
|
},
|
|
328317
328317
|
remote: {
|
|
328318
|
-
touch_enabled: { _description: "Allow mobile devices to reach this Newmark instance
|
|
328318
|
+
touch_enabled: { _description: "Allow mobile devices to reach this Newmark instance on the same LAN / Tailscale network", _type: "boolean", value: true }
|
|
328319
328319
|
},
|
|
328320
328320
|
models: {
|
|
328321
328321
|
providers: { _description: "LLM providers", _type: "array", value: [] },
|
|
@@ -328863,14 +328863,18 @@ var ChatCompletionsAdapter = class {
|
|
|
328863
328863
|
...request.systemPrompt ? [{ role: CHAT_SYSTEM_ROLE, content: request.systemPrompt }] : [],
|
|
328864
328864
|
...openAIChatMessages(request.messages)
|
|
328865
328865
|
];
|
|
328866
|
+
const tools = this.serializeTools(request.tools);
|
|
328866
328867
|
const body = {
|
|
328867
328868
|
model: request.model,
|
|
328868
328869
|
messages,
|
|
328869
328870
|
temperature: request.temperature,
|
|
328870
|
-
max_tokens: request.maxOutputTokens
|
|
328871
|
-
tools: this.serializeTools(request.tools),
|
|
328872
|
-
tool_choice: "auto"
|
|
328871
|
+
max_tokens: request.maxOutputTokens
|
|
328873
328872
|
};
|
|
328873
|
+
if (tools.length) {
|
|
328874
|
+
body.tools = tools;
|
|
328875
|
+
body.tool_choice = "auto";
|
|
328876
|
+
body.parallel_tool_calls = true;
|
|
328877
|
+
}
|
|
328874
328878
|
if (request.reasoningEffort) body.reasoning_effort = request.reasoningEffort;
|
|
328875
328879
|
if (request.sessionId) body.session_id = request.sessionId;
|
|
328876
328880
|
const base2 = request.baseUrl.replace(/\/+$/, "");
|
|
@@ -329124,6 +329128,7 @@ var ResponsesAdapter = class {
|
|
|
329124
329128
|
if (tools.length) {
|
|
329125
329129
|
body.tools = tools;
|
|
329126
329130
|
body.tool_choice = "auto";
|
|
329131
|
+
body.parallel_tool_calls = true;
|
|
329127
329132
|
}
|
|
329128
329133
|
const base2 = request.baseUrl.replace(/\/+$/, "");
|
|
329129
329134
|
return {
|
|
@@ -335757,6 +335762,10 @@ var MODE_SCOPED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
335757
335762
|
"task_read",
|
|
335758
335763
|
"task_create",
|
|
335759
335764
|
"question",
|
|
335765
|
+
"SubAgent",
|
|
335766
|
+
"subagent_create",
|
|
335767
|
+
// Legacy runtime alias. It is no longer published to models because its
|
|
335768
|
+
// generic name collides with the persistent task checklist.
|
|
335760
335769
|
"task",
|
|
335761
335770
|
"subagent_list",
|
|
335762
335771
|
"subagent_read",
|
|
@@ -335790,6 +335799,8 @@ var PLAN_READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
335790
335799
|
"skill",
|
|
335791
335800
|
"linked_plan",
|
|
335792
335801
|
"build_history_query",
|
|
335802
|
+
"SubAgent",
|
|
335803
|
+
"subagent_create",
|
|
335793
335804
|
"task",
|
|
335794
335805
|
"subagent_list",
|
|
335795
335806
|
"subagent_read",
|
|
@@ -335813,7 +335824,8 @@ var CONCURRENCY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
335813
335824
|
"web_fetch",
|
|
335814
335825
|
"git_status",
|
|
335815
335826
|
"file_audit",
|
|
335816
|
-
"repo_security_audit"
|
|
335827
|
+
"repo_security_audit",
|
|
335828
|
+
"SubAgent"
|
|
335817
335829
|
]);
|
|
335818
335830
|
function isConcurrencySafeTool(name50, riskLevel) {
|
|
335819
335831
|
const toolName = String(name50 || "").trim();
|
|
@@ -336981,7 +336993,7 @@ var ToolExecutor = class {
|
|
|
336981
336993
|
remote_root: { type: "string" },
|
|
336982
336994
|
remote_path: { type: "string" }
|
|
336983
336995
|
}, ["action"]),
|
|
336984
|
-
t3("
|
|
336996
|
+
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"]),
|
|
336985
336997
|
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"] } }, []),
|
|
336986
336998
|
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." } }, []),
|
|
336987
336999
|
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" } }, []),
|
|
@@ -337158,7 +337170,8 @@ var ToolExecutor = class {
|
|
|
337158
337170
|
return normalizeToolResult(output, { tool, workspacePath: wsPath, mode: context.mode || "" });
|
|
337159
337171
|
}
|
|
337160
337172
|
validateInvocation(tool, argsStr, _mode = "", inputSchema) {
|
|
337161
|
-
|
|
337173
|
+
const schemaTool = tool === "task" || tool === "subagent_create" ? "SubAgent" : tool;
|
|
337174
|
+
if (inputSchema === void 0 && !isNativeToolEnabled(schemaTool, this.config.nativeToolEnabled())) {
|
|
337162
337175
|
return { ok: false, error: `[tool disabled] ${tool} is disabled in Settings > Tools.` };
|
|
337163
337176
|
}
|
|
337164
337177
|
let parsed;
|
|
@@ -337180,11 +337193,11 @@ var ToolExecutor = class {
|
|
|
337180
337193
|
}, args);
|
|
337181
337194
|
return validation2.ok ? { ok: false, error: "[permission] Question is disabled by fully_autonomous option feedback." } : { ok: false, error: `[tool schema error] ${validation2.error}` };
|
|
337182
337195
|
}
|
|
337183
|
-
const definition = inputSchema === void 0 ? this.definitions().find((candidate) => candidate.function?.name ===
|
|
337196
|
+
const definition = inputSchema === void 0 ? this.definitions().find((candidate) => candidate.function?.name === schemaTool) : { function: { name: tool, parameters: inputSchema } };
|
|
337184
337197
|
if (!definition) {
|
|
337185
337198
|
return { ok: false, error: `[tool unsupported] ${tool || "(missing tool)"} is not available for the ${this.hostProfile.kind} host on ${this.hostProfile.platform}.` };
|
|
337186
337199
|
}
|
|
337187
|
-
const validation = this.argumentValidators.validate(
|
|
337200
|
+
const validation = this.argumentValidators.validate(schemaTool, definition.function.parameters, args);
|
|
337188
337201
|
return validation.ok ? { ok: true, args } : { ok: false, error: `[tool schema error] ${validation.error}` };
|
|
337189
337202
|
}
|
|
337190
337203
|
async execute(tool, argsStr, wsPath, context = {}) {
|
|
@@ -337506,7 +337519,9 @@ var ToolExecutor = class {
|
|
|
337506
337519
|
case "ssh_workspace":
|
|
337507
337520
|
return await this.sshWorkspace(args, wsPath, context.signal);
|
|
337508
337521
|
case "task":
|
|
337509
|
-
|
|
337522
|
+
case "subagent_create":
|
|
337523
|
+
case "SubAgent":
|
|
337524
|
+
return `[SubAgent] SubAgent request accepted: ${g2("name")}`;
|
|
337510
337525
|
case "subagent_send":
|
|
337511
337526
|
return `[subagent_send] Routed to Agent runtime: ${g2("name")}`;
|
|
337512
337527
|
case "subagent_read":
|
|
@@ -338679,6 +338694,7 @@ var SubagentManager = class {
|
|
|
338679
338694
|
queueMicrotask(() => this.pump());
|
|
338680
338695
|
}
|
|
338681
338696
|
bind(options) {
|
|
338697
|
+
if (options.concurrency !== void 0) this.setConcurrencyLimit(options.concurrency);
|
|
338682
338698
|
if (options.executor) this.executor = options.executor;
|
|
338683
338699
|
if (options.onChange) this.onChange = options.onChange;
|
|
338684
338700
|
if (options.persist) this.persist = options.persist;
|
|
@@ -338695,12 +338711,13 @@ var SubagentManager = class {
|
|
|
338695
338711
|
removeRootInboxListener(listener) {
|
|
338696
338712
|
this.rootInboxListeners.delete(listener);
|
|
338697
338713
|
}
|
|
338698
|
-
create(name50, prompt, model, inputMode, agentMode = "build", createdByAgentId = this.rootAgentId, flowName = "", goalObjective = "", flowPc = 0) {
|
|
338714
|
+
create(name50, prompt, model, inputMode, agentMode = "build", createdByAgentId = this.rootAgentId, flowName = "", goalObjective = "", flowPc = 0, buildRunId = "", intelligenceTier = "") {
|
|
338699
338715
|
const id = (0, import_crypto8.randomUUID)();
|
|
338700
338716
|
const shortId = id.replace(/-/g, "").slice(0, 8);
|
|
338701
338717
|
const slug = natureSlug(name50);
|
|
338702
|
-
const
|
|
338703
|
-
const
|
|
338718
|
+
const createdName = String(name50 || "SubAgent").replace(/\s+/g, " ").trim().slice(0, 160) || "SubAgent";
|
|
338719
|
+
const displayName = createdName;
|
|
338720
|
+
const qualifiedName = `${slug}--${id}`;
|
|
338704
338721
|
const stamp = now();
|
|
338705
338722
|
const record = {
|
|
338706
338723
|
id,
|
|
@@ -338708,9 +338725,11 @@ var SubagentManager = class {
|
|
|
338708
338725
|
natureSlug: slug,
|
|
338709
338726
|
displayName,
|
|
338710
338727
|
qualifiedName,
|
|
338711
|
-
name:
|
|
338728
|
+
name: createdName,
|
|
338712
338729
|
conversationId: this.conversationId,
|
|
338713
338730
|
createdByAgentId,
|
|
338731
|
+
buildRunId: String(buildRunId || "").trim() || void 0,
|
|
338732
|
+
intelligenceTier: String(intelligenceTier || "").trim() || void 0,
|
|
338714
338733
|
prompt,
|
|
338715
338734
|
model: model || "default",
|
|
338716
338735
|
inputMode: inputMode || "guide",
|
|
@@ -339033,6 +339052,20 @@ var SubagentManager = class {
|
|
|
339033
339052
|
listAll() {
|
|
339034
339053
|
return [...this.subs.values()].map(cloneRecord);
|
|
339035
339054
|
}
|
|
339055
|
+
activeCountForBuild(buildRunId) {
|
|
339056
|
+
const target = String(buildRunId || "").trim();
|
|
339057
|
+
if (!target) return 0;
|
|
339058
|
+
return [...this.subs.values()].filter(
|
|
339059
|
+
(record) => record.buildRunId === target && (record.status === "queued" || record.status === "working")
|
|
339060
|
+
).length;
|
|
339061
|
+
}
|
|
339062
|
+
setConcurrencyLimit(value) {
|
|
339063
|
+
this.concurrency = Math.max(1, Math.min(16, Math.floor(Number(value) || 4)));
|
|
339064
|
+
this.pump();
|
|
339065
|
+
}
|
|
339066
|
+
concurrencyLimit() {
|
|
339067
|
+
return this.concurrency;
|
|
339068
|
+
}
|
|
339036
339069
|
pauseScheduling() {
|
|
339037
339070
|
if (this.schedulingPaused) return;
|
|
339038
339071
|
this.schedulingPaused = true;
|
|
@@ -340198,7 +340231,8 @@ function inferDomain(name50) {
|
|
|
340198
340231
|
}
|
|
340199
340232
|
if (name50 === "question") return "interaction";
|
|
340200
340233
|
if (name50 === "skill") return "skills";
|
|
340201
|
-
if (name50 === "task") return "subagent";
|
|
340234
|
+
if (name50 === "task" || name50 === "subagent_create" || name50 === "SubAgent") return "subagent";
|
|
340235
|
+
if (/^task_(read|create)$/.test(name50)) return "plan";
|
|
340202
340236
|
if (/^(linked_plan|build_history_query)$/.test(name50)) return "plan";
|
|
340203
340237
|
return "general";
|
|
340204
340238
|
}
|
|
@@ -340462,7 +340496,7 @@ function resetPublicAssistantDeltaFilter(agent) {
|
|
|
340462
340496
|
function prepareAssistantToolVisibility(agent, definitions) {
|
|
340463
340497
|
const names = definitions.map(toolDefinitionName);
|
|
340464
340498
|
brokerOnlyAssistantBuffers.set(agent, {
|
|
340465
|
-
brokerOnly: names.includes(TOOL_PROVISION_NAME) && names.every((name50) => name50 === TOOL_PROVISION_NAME || name50 === "skill" ||
|
|
340499
|
+
brokerOnly: names.includes(TOOL_PROVISION_NAME) && names.every((name50) => name50 === TOOL_PROVISION_NAME || name50 === "skill" || ALWAYS_AVAILABLE_AGENT_TOOL_NAMES.has(name50)),
|
|
340466
340500
|
pending: [],
|
|
340467
340501
|
released: false
|
|
340468
340502
|
});
|
|
@@ -340754,7 +340788,7 @@ async function runAgentKernel(agent) {
|
|
|
340754
340788
|
const requestStartedAt = Date.now();
|
|
340755
340789
|
let firstTokenRecorded = false;
|
|
340756
340790
|
const tools = context.tools || [];
|
|
340757
|
-
const brokerOnlySurface = tools.length > 0 && tools.every((tool) => tool.name === TOOL_PROVISION_NAME || tool.name === "skill" ||
|
|
340791
|
+
const brokerOnlySurface = tools.length > 0 && tools.every((tool) => tool.name === TOOL_PROVISION_NAME || tool.name === "skill" || ALWAYS_AVAILABLE_AGENT_TOOL_NAMES.has(tool.name));
|
|
340758
340792
|
currentAgent.beginRouteAttempt();
|
|
340759
340793
|
try {
|
|
340760
340794
|
const currentProvider = currentAgent.engineModel();
|
|
@@ -340989,6 +341023,8 @@ function buildBuildContextBootstrap(agent, messages, options) {
|
|
|
340989
341023
|
buildConversationTaskLedger(agent),
|
|
340990
341024
|
"## Tool Awareness Bootstrap",
|
|
340991
341025
|
"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.",
|
|
341026
|
+
"Only bash, pwd, read, write, edit, delete_file, glob, and grep are foundational tools with initial full schemas (subject to mode and policy filtering).",
|
|
341027
|
+
"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.",
|
|
340992
341028
|
...catalogLines.length ? catalogLines : ["- No callable tools are available for this provider turn."],
|
|
340993
341029
|
`Necessary full schemas supplied natively for this provider turn: ${activeNames.length ? activeNames.join(", ") : "(none; use tool_provision when its schema is available)"}.`,
|
|
340994
341030
|
"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."
|
|
@@ -341178,7 +341214,8 @@ function routeTransitionNotice(agent, previous) {
|
|
|
341178
341214
|
var TOOL_PROVISION_NAME = "tool_provision";
|
|
341179
341215
|
var INITIAL_TOOL_SCHEMA_LIMIT = 8;
|
|
341180
341216
|
var TOOL_PROVISION_BATCH_LIMIT = 8;
|
|
341181
|
-
var
|
|
341217
|
+
var BASIC_INITIAL_TOOL_NAMES = /* @__PURE__ */ new Set(["bash", "pwd", "read", "write", "edit", "delete_file", "glob", "grep"]);
|
|
341218
|
+
var ALWAYS_AVAILABLE_AGENT_TOOL_NAMES = BASIC_INITIAL_TOOL_NAMES;
|
|
341182
341219
|
var ToolProvisionSession = class {
|
|
341183
341220
|
definitionsByName = /* @__PURE__ */ new Map();
|
|
341184
341221
|
initialNames = /* @__PURE__ */ new Set();
|
|
@@ -341202,7 +341239,7 @@ var ToolProvisionSession = class {
|
|
|
341202
341239
|
const name50 = toolDefinitionName(definition);
|
|
341203
341240
|
if (this.definitionsByName.has(name50)) this.initialNames.add(name50);
|
|
341204
341241
|
}
|
|
341205
|
-
for (const name50 of
|
|
341242
|
+
for (const name50 of ALWAYS_AVAILABLE_AGENT_TOOL_NAMES) {
|
|
341206
341243
|
if (this.definitionsByName.has(name50)) this.initialNames.add(name50);
|
|
341207
341244
|
}
|
|
341208
341245
|
for (const name50 of this.provisionedNames) {
|
|
@@ -341360,29 +341397,6 @@ function toolSurfaceIdentityForAgent(agent) {
|
|
|
341360
341397
|
optionFeedback: agent.config.getStr("agent", "option_feedback")
|
|
341361
341398
|
});
|
|
341362
341399
|
}
|
|
341363
|
-
var CAPABILITY_TO_DOMAIN = {
|
|
341364
|
-
"vcs.inspect": ["git"],
|
|
341365
|
-
"code.search": ["core"],
|
|
341366
|
-
"test.run": ["core"],
|
|
341367
|
-
"web.search": ["web"],
|
|
341368
|
-
"automation.manage": ["automation"],
|
|
341369
|
-
"flow.manage": ["flow"],
|
|
341370
|
-
"memory.manage": ["memory"],
|
|
341371
|
-
"computer.manage": ["computer"],
|
|
341372
|
-
"browser.manage": ["browser"],
|
|
341373
|
-
"terminal.manage": ["general", "core"],
|
|
341374
|
-
"github.manage": ["general"],
|
|
341375
|
-
"ssh.manage": ["general"],
|
|
341376
|
-
"skill.manage": ["skills", "general"],
|
|
341377
|
-
"plan.manage": ["plan"],
|
|
341378
|
-
"history.query": ["plan"],
|
|
341379
|
-
"media.display": ["media"],
|
|
341380
|
-
"interaction.manage": ["interaction"]
|
|
341381
|
-
};
|
|
341382
|
-
var CAPABILITY_TOOL_HINTS = {
|
|
341383
|
-
"skill.manage": ["skill", "skill_download"],
|
|
341384
|
-
"memory.manage": ["memory_lab_read", "memory_lab_query", "memory_lab_update", "memory_lab_reindex"]
|
|
341385
|
-
};
|
|
341386
341400
|
function routeToolSurfaceV2(agent, definitions, toolchain, task) {
|
|
341387
341401
|
if (!agent.shouldExposeToolInterface()) {
|
|
341388
341402
|
return {
|
|
@@ -341394,67 +341408,34 @@ function routeToolSurfaceV2(agent, definitions, toolchain, task) {
|
|
|
341394
341408
|
].join("\n")
|
|
341395
341409
|
};
|
|
341396
341410
|
}
|
|
341397
|
-
|
|
341398
|
-
const
|
|
341399
|
-
|
|
341400
|
-
|
|
341401
|
-
|
|
341402
|
-
|
|
341403
|
-
|
|
341404
|
-
|
|
341405
|
-
|
|
341406
|
-
|
|
341407
|
-
|
|
341408
|
-
|
|
341409
|
-
|
|
341410
|
-
|
|
341411
|
-
|
|
341412
|
-
|
|
341413
|
-
|
|
341414
|
-
for (const toolName of CAPABILITY_TOOL_HINTS[capabilityId] || []) toolHints.add(toolName);
|
|
341415
|
-
}
|
|
341416
|
-
const names = definitions.map(toolDefinitionName);
|
|
341417
|
-
for (const name50 of names) {
|
|
341418
|
-
if (name50 && new RegExp(`(?:^|[^A-Za-z0-9_])${name50.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:$|[^A-Za-z0-9_])`, "i").test(task)) {
|
|
341419
|
-
toolHints.add(name50);
|
|
341420
|
-
}
|
|
341421
|
-
}
|
|
341422
|
-
const selected = definitions.filter((definition) => {
|
|
341423
|
-
const name50 = toolDefinitionName(definition);
|
|
341424
|
-
const descriptor = toolchain.registry.get(name50);
|
|
341425
|
-
if (!descriptor || descriptor.riskLevel === "destructive") return false;
|
|
341426
|
-
if (toolHints.has(name50)) return true;
|
|
341427
|
-
if (!domains.has(descriptor.capabilityId.slice(4))) return false;
|
|
341428
|
-
return true;
|
|
341429
|
-
}).sort((a3, b2) => {
|
|
341430
|
-
const aHint = toolHints.has(toolDefinitionName(a3)) ? 0 : 1;
|
|
341431
|
-
const bHint = toolHints.has(toolDefinitionName(b2)) ? 0 : 1;
|
|
341432
|
-
return aHint - bHint || names.indexOf(toolDefinitionName(a3)) - names.indexOf(toolDefinitionName(b2));
|
|
341433
|
-
}).slice(0, INITIAL_TOOL_SCHEMA_LIMIT);
|
|
341434
|
-
const selectedNames = new Set(selected.map(toolDefinitionName));
|
|
341435
|
-
const core = definitions.filter((definition) => {
|
|
341436
|
-
const name50 = toolDefinitionName(definition);
|
|
341437
|
-
return SUBAGENT_CORE_TOOL_NAMES.has(name50) && !selectedNames.has(name50);
|
|
341438
|
-
});
|
|
341439
|
-
const surface = core.length ? selected.concat(core) : selected;
|
|
341440
|
-
if (surface.length === definitions.length) return { definitions, systemPromptNotice: "" };
|
|
341441
|
-
if (!selected.length) {
|
|
341442
|
-
return {
|
|
341443
|
-
definitions: surface,
|
|
341444
|
-
systemPromptNotice: [
|
|
341445
|
-
"## Tool Interface Availability",
|
|
341446
|
-
"This turn was classified as conversational, so no task-specific tool schema was preloaded.",
|
|
341447
|
-
`The ${TOOL_PROVISION_NAME} interface still exposes the complete compact capability catalog and can provision an original tool schema when the task requires it.`
|
|
341448
|
-
].join("\n")
|
|
341449
|
-
};
|
|
341411
|
+
const surface = definitions.filter((definition) => BASIC_INITIAL_TOOL_NAMES.has(toolDefinitionName(definition)));
|
|
341412
|
+
const advancedCount = Math.max(0, definitions.length - surface.length);
|
|
341413
|
+
let planFingerprint = "";
|
|
341414
|
+
if (toolchain) {
|
|
341415
|
+
const planner = new ToolExposurePlanner(toolchain.registry, toolchain.catalog);
|
|
341416
|
+
const plan = planner.plan({
|
|
341417
|
+
agentRunId: agent.runtimeActorId,
|
|
341418
|
+
buildBlockId: agent.activeConversationId || "build",
|
|
341419
|
+
userInput: task,
|
|
341420
|
+
objective: "",
|
|
341421
|
+
previousToolCalls: [],
|
|
341422
|
+
toolUsageFrequency: /* @__PURE__ */ new Map(),
|
|
341423
|
+
permissionScope: ["workspace"],
|
|
341424
|
+
tokenBudget: 2e4,
|
|
341425
|
+
providerToolLimit: 0
|
|
341426
|
+
});
|
|
341427
|
+
planFingerprint = plan.plan.stableToolsetHash.slice(0, 8);
|
|
341450
341428
|
}
|
|
341451
341429
|
return {
|
|
341452
341430
|
definitions: surface,
|
|
341453
341431
|
systemPromptNotice: [
|
|
341454
341432
|
"## Tool Interface Availability",
|
|
341455
|
-
`
|
|
341456
|
-
|
|
341457
|
-
|
|
341433
|
+
`The initial full-schema surface is restricted to foundational workspace tools: ${surface.map(toolDefinitionName).join(", ") || "(none allowed in this mode)"}.`,
|
|
341434
|
+
`${advancedCount} advanced tools are advertised by capability in the compact ${TOOL_PROVISION_NAME} catalog without loading their schemas.`,
|
|
341435
|
+
"Advanced tools include SubAgent, task tools, Git/GitHub, browser, Computer Use, skills, MCP, automations, Flow, and Memory Lab.",
|
|
341436
|
+
`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.`,
|
|
341437
|
+
"Do not call an advanced tool directly from the initial catalog: capability presence is not callability until provisioning has completed.",
|
|
341438
|
+
planFingerprint ? `Capability routing fingerprint: ${planFingerprint}.` : "Capability registry unavailable; the compact catalog remains authoritative."
|
|
341458
341439
|
].join("\n")
|
|
341459
341440
|
};
|
|
341460
341441
|
}
|
|
@@ -341563,7 +341544,7 @@ var INLINE_TOOL_RESULT_MAX_CHARS = 24e3;
|
|
|
341563
341544
|
function spillOversizedToolResult(agent, name50, text) {
|
|
341564
341545
|
const value = String(text || "");
|
|
341565
341546
|
if (value.length <= INLINE_TOOL_RESULT_MAX_CHARS) return value;
|
|
341566
|
-
if (["computer_use", "browser_use", "pdf_read", "image_inspect", "image_display", "task", "subagent_send", "subagent_result", "subagent_read", "linked_plan", "question"].includes(name50)) {
|
|
341547
|
+
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)) {
|
|
341567
341548
|
return value;
|
|
341568
341549
|
}
|
|
341569
341550
|
const artifactId = agent.storeToolResultArtifact(name50, value);
|
|
@@ -341664,7 +341645,7 @@ async function executeNewmarkTool(agent, name50, args, inputSchema, signal) {
|
|
|
341664
341645
|
if (signal?.aborted) throw abortError4();
|
|
341665
341646
|
return result2;
|
|
341666
341647
|
};
|
|
341667
|
-
if (name50 === "task") return (await agent.handleSubagentEnvelope(args)).output;
|
|
341648
|
+
if (name50 === "task" || name50 === "subagent_create" || name50 === "SubAgent") return (await agent.handleSubagentEnvelope(args, true)).output;
|
|
341668
341649
|
if (name50 === "subagent_send") return (await agent.handleSubagentContinueEnvelope(args)).output;
|
|
341669
341650
|
if (name50 === "subagent_list") return agent.handleSubagentListEnvelope(args).output;
|
|
341670
341651
|
if (name50 === "subagent_read") return agent.handleSubagentReadEnvelope(args).output;
|
|
@@ -344816,7 +344797,7 @@ var Agent4 = class _Agent {
|
|
|
344816
344797
|
this.tools = new ToolExecutor(rootPath, this.config, this.ssh, this.workspace);
|
|
344817
344798
|
this.skills = new SkillsManager(rootPath);
|
|
344818
344799
|
this.memoryLab = new MemoryLabManager(rootPath, this.config.getStr("general", "language"));
|
|
344819
|
-
this.subagents = new SubagentManager({ rootAgentId: this.runtimeActorId });
|
|
344800
|
+
this.subagents = new SubagentManager({ rootAgentId: this.runtimeActorId, concurrency: this.subagentConcurrencyLimit() });
|
|
344820
344801
|
if (this.mode === "goal" && !this.goal) {
|
|
344821
344802
|
this.goal = new GoalStateImpl("Set your objective");
|
|
344822
344803
|
}
|
|
@@ -345415,6 +345396,7 @@ var Agent4 = class _Agent {
|
|
|
345415
345396
|
}
|
|
345416
345397
|
setIntelligence(tier, persist = false) {
|
|
345417
345398
|
this.intelligence = normalizeIntelligenceTier(tier);
|
|
345399
|
+
this.subagents?.setConcurrencyLimit(this.subagentConcurrencyLimit());
|
|
345418
345400
|
if (persist) {
|
|
345419
345401
|
this.config.set("models", "default_intelligence", this.intelligence);
|
|
345420
345402
|
this.config.save();
|
|
@@ -345619,12 +345601,17 @@ var Agent4 = class _Agent {
|
|
|
345619
345601
|
return path28.join(ws.path, "conversations", "state.json");
|
|
345620
345602
|
}
|
|
345621
345603
|
workspaceConversationStateKey(conversationId = this.activeConversationId) {
|
|
345622
|
-
|
|
345604
|
+
return this.workspaceConversationStateKeyFor(conversationId, this.workspace.current);
|
|
345605
|
+
}
|
|
345606
|
+
workspaceConversationStateKeyFor(conversationId, ws) {
|
|
345607
|
+
const prefix = this.workspaceConversationPrefixFor(ws);
|
|
345623
345608
|
if (!prefix) return null;
|
|
345624
345609
|
return `${prefix}-${this.safeConversationId(conversationId)}`;
|
|
345625
345610
|
}
|
|
345626
345611
|
workspaceConversationPrefix() {
|
|
345627
|
-
|
|
345612
|
+
return this.workspaceConversationPrefixFor(this.workspace.current);
|
|
345613
|
+
}
|
|
345614
|
+
workspaceConversationPrefixFor(ws) {
|
|
345628
345615
|
if (!ws) return null;
|
|
345629
345616
|
const supplied = String(ws.conversationStatePrefix || "").trim();
|
|
345630
345617
|
if (/^(?:internal|external)-[a-f0-9]{16}$/i.test(supplied)) return supplied.toLowerCase();
|
|
@@ -347074,8 +347061,49 @@ ${String(event.toolArgs || "")}`;
|
|
|
347074
347061
|
}
|
|
347075
347062
|
}
|
|
347076
347063
|
listConversationStates() {
|
|
347077
|
-
|
|
347078
|
-
|
|
347064
|
+
return this.listWorkspaceConversationStates(this.workspace.current);
|
|
347065
|
+
}
|
|
347066
|
+
subagentConcurrencyLimit() {
|
|
347067
|
+
return this.intelligence === "ultra" ? 16 : 4;
|
|
347068
|
+
}
|
|
347069
|
+
/** 当前工作区使用内存中的前台选择;后台工作区从各自持久化状态读取 active id。 */
|
|
347070
|
+
activeConversationIdForWorkspace(ws) {
|
|
347071
|
+
const targetWs = ws || this.workspace.current;
|
|
347072
|
+
if (!targetWs) return "default";
|
|
347073
|
+
const currentWs = this.workspace.current;
|
|
347074
|
+
const isCurrent = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(targetWs.path);
|
|
347075
|
+
if (isCurrent) return this.safeConversationId(this.activeConversationId || "default");
|
|
347076
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347077
|
+
return this.safeConversationId(stored.activeConversationId || "default");
|
|
347078
|
+
}
|
|
347079
|
+
/**
|
|
347080
|
+
* 持久化的完整 work run 记录(含 interrupted/force_interrupted)。
|
|
347081
|
+
* 不依赖运行时内存(run 结束后内存清空,state 端点曾因此丢失被中断的构建记录),
|
|
347082
|
+
* 供 mobile 端点稳定透出完整对话信息;移动端按同一格式解析。
|
|
347083
|
+
*/
|
|
347084
|
+
getPersistedConversationWorkRuns(conversationId, ws = null) {
|
|
347085
|
+
const targetWs = ws || this.workspace.current;
|
|
347086
|
+
const clean = this.safeConversationId(conversationId || "default");
|
|
347087
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, targetWs);
|
|
347088
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347089
|
+
const persisted = stateKey2 && stored.conversations ? stored.conversations[stateKey2] : void 0;
|
|
347090
|
+
const tree = persisted ? this.normalizeConversationTree(persisted) : null;
|
|
347091
|
+
const runtimeNodeId = String(tree?.activeNodeId || "");
|
|
347092
|
+
const viewedNodeId = tree ? this.resolveConversationTreePath(tree, this.storedConversationTreePath(tree, persisted?.viewedBranchNodePath, runtimeNodeId)) : "";
|
|
347093
|
+
const viewedNode = tree?.nodes[viewedNodeId];
|
|
347094
|
+
return this.normalizeWorkRuns(viewedNode?.workRuns || persisted?.workRuns);
|
|
347095
|
+
}
|
|
347096
|
+
/** Exact membership check against the raw persisted state key, before UI content deduplication. */
|
|
347097
|
+
hasConversationInWorkspace(conversationId, ws) {
|
|
347098
|
+
const targetWs = ws || this.workspace.current;
|
|
347099
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(this.safeConversationId(conversationId || "default"), targetWs);
|
|
347100
|
+
if (!stateKey2) return false;
|
|
347101
|
+
return Object.prototype.hasOwnProperty.call(this.readStoredConversationState(targetWs).conversations || {}, stateKey2);
|
|
347102
|
+
}
|
|
347103
|
+
/** 按工作区列对话(任意 ws,key 前缀 = kind-sha256(path).slice(0,16));供 mobile API 透出工作区从属对话 */
|
|
347104
|
+
listWorkspaceConversationStates(ws) {
|
|
347105
|
+
const stored = this.readStoredConversationState(ws);
|
|
347106
|
+
const prefix = this.workspaceConversationPrefixFor(ws) || "";
|
|
347079
347107
|
const scopedEntries = Object.entries(stored.conversations || {}).filter(([key3]) => !prefix || key3.startsWith(prefix));
|
|
347080
347108
|
if (scopedEntries.some(([, value]) => !Number.isFinite(value.order))) {
|
|
347081
347109
|
const legacyOrder = [...scopedEntries].sort(([, a3], [, b2]) => {
|
|
@@ -347086,7 +347114,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
347086
347114
|
legacyOrder.forEach(([, value], index) => {
|
|
347087
347115
|
value.order = index;
|
|
347088
347116
|
});
|
|
347089
|
-
this.writeStoredConversationState(stored);
|
|
347117
|
+
this.writeStoredConversationState(stored, ws);
|
|
347090
347118
|
}
|
|
347091
347119
|
const rows = [];
|
|
347092
347120
|
for (const [key3, value] of Object.entries(stored.conversations || {})) {
|
|
@@ -347140,6 +347168,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
347140
347168
|
this.subagents = sharedSubagentManager(this.subagentManagerKey(clean), {
|
|
347141
347169
|
conversationId: clean,
|
|
347142
347170
|
rootAgentId: state?.rootAgentId || this.runtimeActorId,
|
|
347171
|
+
concurrency: this.subagentConcurrencyLimit(),
|
|
347143
347172
|
state,
|
|
347144
347173
|
executor: (job) => this.runSubagentJob(job.record.id, job.prompt, job.flowName, job.reason),
|
|
347145
347174
|
persist: (subagentState) => this.persistSubagentState(clean, subagentState),
|
|
@@ -347209,15 +347238,14 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347209
347238
|
}
|
|
347210
347239
|
getConversationSnapshot(conversationId = this.activeConversationId, options = {}) {
|
|
347211
347240
|
const clean = this.safeConversationId(conversationId || "default");
|
|
347212
|
-
const
|
|
347213
|
-
const
|
|
347214
|
-
const
|
|
347215
|
-
|
|
347216
|
-
|
|
347217
|
-
|
|
347218
|
-
})();
|
|
347241
|
+
const ws = options.workspace || this.workspace.current;
|
|
347242
|
+
const currentWs = this.workspace.current;
|
|
347243
|
+
const isActiveWorkspace = !ws && !currentWs || !!ws && !!currentWs && path28.resolve(ws.path) === path28.resolve(currentWs.path);
|
|
347244
|
+
const isActiveConversation = isActiveWorkspace && clean === this.safeConversationId(this.activeConversationId || "default");
|
|
347245
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, ws);
|
|
347246
|
+
const memoryKey = ws ? `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}` : null;
|
|
347219
347247
|
const memory = memoryKey ? this.workspaceConversations.get(memoryKey) : void 0;
|
|
347220
|
-
const stored = this.readStoredConversationState();
|
|
347248
|
+
const stored = this.readStoredConversationState(ws);
|
|
347221
347249
|
const persisted = stateKey2 && stored.conversations ? stored.conversations[stateKey2] : void 0;
|
|
347222
347250
|
const tree = persisted ? this.normalizeConversationTree(persisted) : null;
|
|
347223
347251
|
const runtimeNodeId = String(tree?.activeNodeId || "");
|
|
@@ -347237,7 +347265,7 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347237
347265
|
const continuations = this.normalizeContinuations(isActiveConversation && viewingRuntimeNode ? this.continuations : viewedNode?.continuations || persisted?.continuations || memory?.continuations);
|
|
347238
347266
|
return {
|
|
347239
347267
|
conversationId: clean,
|
|
347240
|
-
conversations: this.
|
|
347268
|
+
conversations: this.listWorkspaceConversationStates(ws),
|
|
347241
347269
|
conversationPlan: this.normalizeConversationPlan(isActiveConversation ? this.conversationPlan : persisted?.plan || memory?.plan),
|
|
347242
347270
|
linkedPlan: this.normalizeLinkedPlan(isActiveConversation ? this.linkedPlan : persisted?.linkedPlan || memory?.linkedPlan),
|
|
347243
347271
|
subagents: this.recordsForState(isActiveConversation ? this.subagents.serialize() : persisted?.subagentState || memory?.subagentState),
|
|
@@ -347247,9 +347275,9 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347247
347275
|
historyMessages: history.length,
|
|
347248
347276
|
workRuns,
|
|
347249
347277
|
continuations,
|
|
347250
|
-
modelSelection: isActiveConversation ? this.currentConversationModelSelection() : persisted?.modelSelection || memory?.modelSelection ||
|
|
347278
|
+
modelSelection: isActiveConversation ? this.currentConversationModelSelection() : persisted?.modelSelection || memory?.modelSelection || { kind: "auto" },
|
|
347251
347279
|
flowSelection: isActiveConversation ? this.currentConversationFlowSelection() : persisted?.flowSelection || memory?.flowSelection || null,
|
|
347252
|
-
inputMode: this.inputMode,
|
|
347280
|
+
inputMode: isActiveConversation ? this.inputMode : persisted?.inputMode || memory?.inputMode || "guide",
|
|
347253
347281
|
mode: isActiveConversation ? this.mode : persisted?.mode || memory?.mode || "build",
|
|
347254
347282
|
goal: isActiveConversation ? this.serializeGoal() : persisted?.goal || memory?.goal || null,
|
|
347255
347283
|
branches: this.branchGroupMetadata(tree),
|
|
@@ -347536,12 +347564,16 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347536
347564
|
if (clean === this.safeConversationId(this.activeConversationId)) this.setConversationFromStorage(clean);
|
|
347537
347565
|
return this.getConversationSnapshot(clean);
|
|
347538
347566
|
}
|
|
347539
|
-
setConversationPinned(id, pinned) {
|
|
347567
|
+
setConversationPinned(id, pinned, ws = this.workspace.current) {
|
|
347568
|
+
const targetWs = ws || this.workspace.current;
|
|
347569
|
+
if (!targetWs) return false;
|
|
347540
347570
|
const clean = this.safeConversationId(id || "default");
|
|
347541
|
-
this.
|
|
347542
|
-
const
|
|
347571
|
+
const currentWs = this.workspace.current;
|
|
347572
|
+
const isCurrent = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(targetWs.path);
|
|
347573
|
+
if (isCurrent) this.saveWorkspaceConversationState();
|
|
347574
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, targetWs);
|
|
347543
347575
|
if (!stateKey2) return false;
|
|
347544
|
-
const stored = this.readStoredConversationState();
|
|
347576
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347545
347577
|
stored.conversations = stored.conversations || {};
|
|
347546
347578
|
const existing = stored.conversations[stateKey2];
|
|
347547
347579
|
if (!existing) return false;
|
|
@@ -347550,23 +347582,65 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347550
347582
|
const siblingOrders = Object.entries(stored.conversations).filter(([key3, value]) => key3 !== stateKey2 && !!value.pinned === existing.pinned && Number.isFinite(value.order)).map(([, value]) => Number(value.order));
|
|
347551
347583
|
existing.order = siblingOrders.length ? Math.min(...siblingOrders) - 1 : 0;
|
|
347552
347584
|
existing.updatedAt = existing.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
347553
|
-
this.writeStoredConversationState(stored);
|
|
347585
|
+
this.writeStoredConversationState(stored, targetWs);
|
|
347554
347586
|
return true;
|
|
347555
347587
|
}
|
|
347556
|
-
renameConversation(id, title) {
|
|
347588
|
+
renameConversation(id, title, ws = this.workspace.current) {
|
|
347589
|
+
const targetWs = ws || this.workspace.current;
|
|
347590
|
+
if (!targetWs) return false;
|
|
347557
347591
|
const clean = this.safeConversationId(id || "default");
|
|
347558
347592
|
const nextTitle = String(title || "").replace(/\s+/g, " ").trim().slice(0, 80);
|
|
347559
347593
|
if (!nextTitle) return false;
|
|
347560
|
-
this.
|
|
347561
|
-
const
|
|
347594
|
+
const currentWs = this.workspace.current;
|
|
347595
|
+
const isCurrent = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(targetWs.path);
|
|
347596
|
+
if (isCurrent) this.saveWorkspaceConversationState();
|
|
347597
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, targetWs);
|
|
347562
347598
|
if (!stateKey2) return false;
|
|
347563
|
-
const stored = this.readStoredConversationState();
|
|
347599
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347564
347600
|
const existing = stored.conversations?.[stateKey2];
|
|
347565
347601
|
if (!existing) return false;
|
|
347566
347602
|
existing.title = nextTitle;
|
|
347567
|
-
this.writeStoredConversationState(stored);
|
|
347603
|
+
this.writeStoredConversationState(stored, targetWs);
|
|
347568
347604
|
return true;
|
|
347569
347605
|
}
|
|
347606
|
+
/** 为指定工作区创建空白对话,不临时切换全局前台工作区。 */
|
|
347607
|
+
createConversationInWorkspace(ws, title = "") {
|
|
347608
|
+
const existing = this.listWorkspaceConversationStates(ws);
|
|
347609
|
+
const id = this.safeConversationId(`conv-${Date.now()}-${crypto14.randomUUID().slice(0, 8)}`);
|
|
347610
|
+
const resolvedTitle = String(title || "").replace(/\s+/g, " ").trim().slice(0, 80) || `New chat ${existing.length + 1}`;
|
|
347611
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(id, ws);
|
|
347612
|
+
if (!stateKey2) throw new Error("Conversation workspace is unavailable.");
|
|
347613
|
+
const unpinnedOrders = existing.filter((item) => !item.pinned).map((item) => Number(item.order || 0));
|
|
347614
|
+
const order = unpinnedOrders.length ? Math.min(...unpinnedOrders) - 1 : 0;
|
|
347615
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
347616
|
+
this.mutateStoredConversationState(ws, (latest) => {
|
|
347617
|
+
latest.version = 3;
|
|
347618
|
+
latest.activeConversationId = id;
|
|
347619
|
+
latest.conversations = latest.conversations || {};
|
|
347620
|
+
latest.conversations[stateKey2] = {
|
|
347621
|
+
title: resolvedTitle,
|
|
347622
|
+
chatMessages: [],
|
|
347623
|
+
history: [],
|
|
347624
|
+
plan: { items: [] },
|
|
347625
|
+
linkedPlan: { markdown: "", revision: 0 },
|
|
347626
|
+
workRuns: [],
|
|
347627
|
+
continuations: [],
|
|
347628
|
+
inputMode: this.defaultInputMode(),
|
|
347629
|
+
mode: "build",
|
|
347630
|
+
updatedAt: now2,
|
|
347631
|
+
pinned: false,
|
|
347632
|
+
pinnedAt: "",
|
|
347633
|
+
order,
|
|
347634
|
+
branchCommunication: false
|
|
347635
|
+
};
|
|
347636
|
+
return latest;
|
|
347637
|
+
});
|
|
347638
|
+
const currentWs = this.workspace.current;
|
|
347639
|
+
if (currentWs && path28.resolve(currentWs.path) === path28.resolve(ws.path)) {
|
|
347640
|
+
this.setConversationFromStorage(id);
|
|
347641
|
+
}
|
|
347642
|
+
return { id, title: resolvedTitle };
|
|
347643
|
+
}
|
|
347570
347644
|
/**
|
|
347571
347645
|
* 首 Build 命名判定:仅当 (1) 当前对话尚无历史 Build(排除当前 run)且
|
|
347572
347646
|
* (2) 其持久化 title 仍是自动生成(含为空)时返回 true。dev-0.4.3 起不再
|
|
@@ -347677,6 +347751,34 @@ Conversation title (a few words):`;
|
|
|
347677
347751
|
this.writeStoredConversationState(stored);
|
|
347678
347752
|
return true;
|
|
347679
347753
|
}
|
|
347754
|
+
/** Reorder one pinned group inside an explicit workspace without changing membership. */
|
|
347755
|
+
reorderWorkspaceConversationGroup(ids, ws) {
|
|
347756
|
+
const targetWs = ws || this.workspace.current;
|
|
347757
|
+
if (!targetWs || !Array.isArray(ids) || ids.length < 2) return false;
|
|
347758
|
+
const normalized = ids.map((id) => this.safeConversationId(String(id || "")));
|
|
347759
|
+
if (normalized.some((id) => !id) || new Set(normalized).size !== normalized.length) return false;
|
|
347760
|
+
const currentWs = this.workspace.current;
|
|
347761
|
+
const isCurrent = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(targetWs.path);
|
|
347762
|
+
if (isCurrent) this.saveWorkspaceConversationState();
|
|
347763
|
+
let accepted = false;
|
|
347764
|
+
this.mutateStoredConversationState(targetWs, (latest) => {
|
|
347765
|
+
const prefix = this.workspaceConversationPrefixFor(targetWs) || "";
|
|
347766
|
+
const entries = Object.entries(latest.conversations || {}).filter(([key3]) => !prefix || key3.startsWith(prefix));
|
|
347767
|
+
const entryById = new Map(entries.map(([key3, value]) => [key3.slice(prefix.length + 1) || key3, value]));
|
|
347768
|
+
const requested = normalized.map((id) => entryById.get(id));
|
|
347769
|
+
if (requested.some((entry) => !entry)) return latest;
|
|
347770
|
+
if (new Set(requested.map((entry) => !!entry.pinned)).size !== 1) return latest;
|
|
347771
|
+
const orderSlots = requested.map((entry) => Number(entry.order));
|
|
347772
|
+
if (orderSlots.some((order) => !Number.isFinite(order))) return latest;
|
|
347773
|
+
orderSlots.sort((a3, b2) => a3 - b2);
|
|
347774
|
+
normalized.forEach((id, index) => {
|
|
347775
|
+
entryById.get(id).order = orderSlots[index];
|
|
347776
|
+
});
|
|
347777
|
+
accepted = true;
|
|
347778
|
+
return latest;
|
|
347779
|
+
});
|
|
347780
|
+
return accepted;
|
|
347781
|
+
}
|
|
347680
347782
|
flushConversationState() {
|
|
347681
347783
|
this.saveWorkspaceConversationState();
|
|
347682
347784
|
}
|
|
@@ -349625,21 +349727,21 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
349625
349727
|
this.saveWorkspaceConversationState(true);
|
|
349626
349728
|
return { text, hiddenUserInput: true, goalContinuation: true };
|
|
349627
349729
|
}
|
|
349628
|
-
buildSessionArchive(messages,
|
|
349730
|
+
buildSessionArchive(messages, context, archiveDir) {
|
|
349629
349731
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace("T", "_").replace("Z", "");
|
|
349630
349732
|
const filename = `session_${stamp}_${crypto14.randomUUID().slice(0, 8)}.md`;
|
|
349631
349733
|
let markdown = `# Newmark Session \u2014 ${stamp}
|
|
349632
349734
|
|
|
349633
349735
|
`;
|
|
349634
|
-
markdown += `**Mode**: ${mode}
|
|
349635
|
-
**Model**: ${model}
|
|
349736
|
+
markdown += `**Mode**: ${context.mode}
|
|
349737
|
+
**Model**: ${context.model}
|
|
349636
349738
|
`;
|
|
349637
349739
|
markdown += `**Messages**: ${messages.length}
|
|
349638
349740
|
|
|
349639
349741
|
---
|
|
349640
349742
|
|
|
349641
349743
|
`;
|
|
349642
|
-
if (
|
|
349744
|
+
if (context.goal?.objective) markdown += `**Goal**: ${context.goal.objective}
|
|
349643
349745
|
|
|
349644
349746
|
`;
|
|
349645
349747
|
for (const msg of messages) {
|
|
@@ -349662,12 +349764,12 @@ ${msg.content}
|
|
|
349662
349764
|
writeSessionArchive(messages, mode, model) {
|
|
349663
349765
|
const archiveDir = this.archiveDir();
|
|
349664
349766
|
fs25.mkdirSync(archiveDir, { recursive: true });
|
|
349665
|
-
const archive = this.buildSessionArchive(messages, mode, model, archiveDir);
|
|
349767
|
+
const archive = this.buildSessionArchive(messages, { mode, model, goal: this.serializeGoal() }, archiveDir);
|
|
349666
349768
|
fs25.writeFileSync(path28.join(archiveDir, archive.filename), archive.markdown, "utf-8");
|
|
349667
349769
|
return archive.filename;
|
|
349668
349770
|
}
|
|
349669
|
-
async writeSessionArchiveAsync(messages,
|
|
349670
|
-
const archive = this.buildSessionArchive(messages,
|
|
349771
|
+
async writeSessionArchiveAsync(messages, context, archiveDir = this.archiveDir()) {
|
|
349772
|
+
const archive = this.buildSessionArchive(messages, context, archiveDir);
|
|
349671
349773
|
await fs25.promises.mkdir(archiveDir, { recursive: true });
|
|
349672
349774
|
const outPath = path28.join(archiveDir, archive.filename);
|
|
349673
349775
|
const tempPath = `${outPath}.${process.pid}.${crypto14.randomUUID()}.tmp`;
|
|
@@ -349754,30 +349856,36 @@ ${msg.content}
|
|
|
349754
349856
|
* large markdown payload and manifest use promise-based filesystem I/O so
|
|
349755
349857
|
* independent workspaces can archive in parallel without freezing Electron.
|
|
349756
349858
|
*/
|
|
349757
|
-
async archiveConversationAsync(conversationId) {
|
|
349758
|
-
return await this.archiveConversationAsyncUnlocked(conversationId);
|
|
349859
|
+
async archiveConversationAsync(conversationId, ws = this.workspace.current) {
|
|
349860
|
+
return await this.archiveConversationAsyncUnlocked(conversationId, ws);
|
|
349759
349861
|
}
|
|
349760
|
-
async archiveConversationAsyncUnlocked(conversationId) {
|
|
349761
|
-
const ws = this.workspace.current;
|
|
349862
|
+
async archiveConversationAsyncUnlocked(conversationId, targetWorkspace = this.workspace.current) {
|
|
349863
|
+
const ws = targetWorkspace || this.workspace.current;
|
|
349762
349864
|
if (!ws) return null;
|
|
349763
349865
|
const clean = this.safeConversationId(conversationId || "default");
|
|
349764
|
-
const stateKey2 = this.
|
|
349866
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, ws);
|
|
349765
349867
|
if (!stateKey2) return null;
|
|
349766
349868
|
const memoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
349767
349869
|
const archiveDir = path28.join(ws.path, "archive");
|
|
349768
|
-
const workspacePrefix = this.
|
|
349769
|
-
const archiveMode = this.modeName();
|
|
349770
|
-
const archiveModel = this.model;
|
|
349870
|
+
const workspacePrefix = this.workspaceConversationPrefixFor(ws) || "";
|
|
349771
349871
|
const cachedStored = this.readStoredConversationState(ws);
|
|
349772
349872
|
const stored = JSON.parse(JSON.stringify(cachedStored || {}));
|
|
349773
349873
|
const persisted = stored.conversations?.[stateKey2];
|
|
349774
349874
|
if (persisted) this.normalizeConversationTree(persisted);
|
|
349775
349875
|
const memory = this.workspaceConversations.get(memoryKey);
|
|
349876
|
+
const archiveMode = persisted?.mode || memory?.mode || "build";
|
|
349877
|
+
const archiveSelection = persisted?.modelSelection || memory?.modelSelection;
|
|
349878
|
+
const archiveModel = archiveSelection?.kind === "deployment" ? archiveSelection.modelId : archiveSelection?.kind === "auto" ? "auto" : "auto";
|
|
349879
|
+
const archiveGoal = persisted?.goal || memory?.goal || null;
|
|
349776
349880
|
const persistedMessagesAvailable = persisted?.chatMessages !== void 0;
|
|
349777
349881
|
const sourceMessages = persisted?.chatMessages ?? memory?.chatMessages ?? [];
|
|
349778
349882
|
const sourceHistory = persistedMessagesAvailable ? persisted?.history ?? [] : memory?.history ?? persisted?.history ?? [];
|
|
349779
349883
|
const messages = this.normalizeConversationChatMessages(sourceMessages, sourceHistory);
|
|
349780
|
-
const filename = await this.writeSessionArchiveAsync(messages,
|
|
349884
|
+
const filename = await this.writeSessionArchiveAsync(messages, {
|
|
349885
|
+
mode: archiveMode,
|
|
349886
|
+
model: archiveModel,
|
|
349887
|
+
goal: archiveGoal
|
|
349888
|
+
}, archiveDir);
|
|
349781
349889
|
const archiveEntry = persisted ? JSON.parse(JSON.stringify(persisted)) : {
|
|
349782
349890
|
title: this.titleFromMessages(messages, clean),
|
|
349783
349891
|
chatMessages: messages,
|
|
@@ -349830,7 +349938,9 @@ ${msg.content}
|
|
|
349830
349938
|
this.workspaceConversations.delete(memoryKey);
|
|
349831
349939
|
const duplicateMemoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
349832
349940
|
this.workspaceConversations.delete(duplicateMemoryKey);
|
|
349833
|
-
|
|
349941
|
+
const currentWs = this.workspace.current;
|
|
349942
|
+
const isCurrentWorkspace = !!currentWs && path28.resolve(currentWs.path) === path28.resolve(ws.path);
|
|
349943
|
+
if (isCurrentWorkspace && clean === this.safeConversationId(this.activeConversationId || "default")) {
|
|
349834
349944
|
this.activeConversationId = nextActiveId || "default";
|
|
349835
349945
|
this.loadWorkspaceConversationState();
|
|
349836
349946
|
}
|
|
@@ -351075,7 +351185,7 @@ ${persisted.map((guide, index) => `Guide ${index + 1}: ${guide.text}`).join("\n"
|
|
|
351075
351185
|
return `${accepted.output}
|
|
351076
351186
|
${settled?.result || settled?.error || ""}`.trim();
|
|
351077
351187
|
}
|
|
351078
|
-
async handleSubagentEnvelope(args) {
|
|
351188
|
+
async handleSubagentEnvelope(args, requireRunningBuild = false) {
|
|
351079
351189
|
try {
|
|
351080
351190
|
const params = JSON.parse(args);
|
|
351081
351191
|
const preset = this.resolveSubagentPreset(params);
|
|
@@ -351090,6 +351200,36 @@ ${settled?.result || settled?.error || ""}`.trim();
|
|
|
351090
351200
|
const requestedModel = this.normalizeSubagentModelSelection(params.model || preset?.model || inheritedModel);
|
|
351091
351201
|
const activeDeployment = this.activeDeployment();
|
|
351092
351202
|
const peerModel = requestedModel !== "auto" && !parseDeploymentSelectionValue2(requestedModel) && activeDeployment && requestedModel === activeDeployment.modelId ? `deployment:${encodeURIComponent(activeDeployment.providerId)}:${encodeURIComponent(activeDeployment.modelId)}` : requestedModel;
|
|
351203
|
+
const buildRunId = this.currentWorkRunId();
|
|
351204
|
+
const runningBuild = buildRunId ? this.workRuns.find((run) => run.runId === buildRunId && run.status === "running") : void 0;
|
|
351205
|
+
const limit = this.subagentConcurrencyLimit();
|
|
351206
|
+
if (requireRunningBuild && !runningBuild) {
|
|
351207
|
+
return {
|
|
351208
|
+
ok: false,
|
|
351209
|
+
output: "[SubAgent terminated] No running Build Block owns this call; no SubAgent was created.",
|
|
351210
|
+
error: "SubAgent tool calls require a running Build Block.",
|
|
351211
|
+
metadata: { kind: "subagent", buildRunId: buildRunId || "", limit, terminated: true }
|
|
351212
|
+
};
|
|
351213
|
+
}
|
|
351214
|
+
if (buildRunId && !runningBuild) {
|
|
351215
|
+
return {
|
|
351216
|
+
ok: false,
|
|
351217
|
+
output: `[SubAgent terminated] Build Block ${buildRunId} is not running; no SubAgent was created.`,
|
|
351218
|
+
error: "SubAgent creation requires a running Build Block.",
|
|
351219
|
+
metadata: { kind: "subagent", buildRunId, limit, terminated: true }
|
|
351220
|
+
};
|
|
351221
|
+
}
|
|
351222
|
+
if (runningBuild) {
|
|
351223
|
+
const activeForBuild = this.subagents.activeCountForBuild(buildRunId);
|
|
351224
|
+
if (activeForBuild >= limit) {
|
|
351225
|
+
return {
|
|
351226
|
+
ok: false,
|
|
351227
|
+
output: `[SubAgent terminated] Build Block ${buildRunId} reached the ${this.intelligence === "ultra" ? "Ultra" : "non-Ultra"} hard limit (${limit}); no SubAgent was created or queued.`,
|
|
351228
|
+
error: `SubAgent hard limit reached for Build Block ${buildRunId}: ${activeForBuild}/${limit}.`,
|
|
351229
|
+
metadata: { kind: "subagent", buildRunId, intelligence: this.intelligence, activeForBuild, limit, terminated: true }
|
|
351230
|
+
};
|
|
351231
|
+
}
|
|
351232
|
+
}
|
|
351093
351233
|
const id = this.subagents.create(
|
|
351094
351234
|
name50,
|
|
351095
351235
|
prompt,
|
|
@@ -351099,7 +351239,9 @@ ${settled?.result || settled?.error || ""}`.trim();
|
|
|
351099
351239
|
this.runtimeActorId,
|
|
351100
351240
|
peerFlow,
|
|
351101
351241
|
peerGoal,
|
|
351102
|
-
Number(params.flow_pc ?? params.flowPc ?? (peerMode === "flow" ? this.flowPc : 0))
|
|
351242
|
+
Number(params.flow_pc ?? params.flowPc ?? (peerMode === "flow" ? this.flowPc : 0)),
|
|
351243
|
+
runningBuild?.runId || "",
|
|
351244
|
+
this.intelligence
|
|
351103
351245
|
);
|
|
351104
351246
|
const sa = this.subagents.get(id);
|
|
351105
351247
|
if (sa && preset) {
|
|
@@ -352407,7 +352549,7 @@ ${custom}`);
|
|
|
352407
352549
|
if (this.intelligence === "ultra") {
|
|
352408
352550
|
parts.push([
|
|
352409
352551
|
"[Ultra Intelligence \u2013 Orchestrator Role]",
|
|
352410
|
-
"You are the lead orchestrator. Actively decompose complex tasks into parallel sub-tasks. Use the `
|
|
352552
|
+
"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.",
|
|
352411
352553
|
"Do not attempt to do all the work yourself. Use SubAgents for parallel investigation, verification, implementation, and review."
|
|
352412
352554
|
].join("\n"));
|
|
352413
352555
|
}
|
|
@@ -352513,7 +352655,7 @@ ${custom}`);
|
|
|
352513
352655
|
"- 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.",
|
|
352514
352656
|
"- 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.",
|
|
352515
352657
|
"- 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.",
|
|
352516
|
-
`- Skills and subagents: skill searches enabled metadata and loads one SKILL.md body on demand; skill_download installs offline skill folders;
|
|
352658
|
+
`- 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.`,
|
|
352517
352659
|
`- Visible output contract: assistant replies are sanitized before display to remove hidden-reasoning markers. ${visibleOutputContract}`,
|
|
352518
352660
|
"- 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."
|
|
352519
352661
|
].join("\n");
|