newmark-agent 0.4.5 → 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.d.ts +1 -1
- package/dist/cli-commands.js +90 -7
- package/dist/cli-discovery.js +10 -0
- package/dist/conversation-utility-host.bundle.cjs +293 -148
- 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 +3 -0
- package/dist/core/dshCompatibility.d.ts +23 -6
- package/dist/core/dshCompatibility.js +99 -1
- package/dist/core/installUpdate.d.ts +67 -0
- package/dist/core/installUpdate.js +268 -0
- package/dist/core/mobilePairing.d.ts +47 -0
- package/dist/core/mobilePairing.js +221 -0
- 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 +64 -3
- package/dist/preload.js +5 -0
- 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 +721 -3
- package/dist/toolchain/registry-seeder.js +3 -1
- package/dist/tools/index.js +11 -5
- package/dist/tools/nativeTools.js +1 -1
- package/dist/tui/src/adapters/core-runtime-adapter.js +17 -1
- package/dist/tui/src/app.js +41 -0
- package/dist/tui/src/data.js +1 -0
- package/dist/tui/src/render.js +11 -0
- package/dist/tui/src/settings-schema.js +3 -1
- package/dist/tui/src/state.js +21 -1
- package/dist/ui/index.html +530 -101
- package/dist/ui/lucide-sprite.svg +10 -0
- package/dist/wsl-agent-host.bundle.cjs +293 -148
- package/package.json +14 -8
|
@@ -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" },
|
|
@@ -328314,6 +328314,9 @@ function defaultConfig() {
|
|
|
328314
328314
|
default_input: { _description: "Default input mode", _type: "choice", _values: ["guide", "next"], value: "guide" },
|
|
328315
328315
|
auto_archive_on_close: { _description: "Auto archive on close", _type: "boolean", value: true }
|
|
328316
328316
|
},
|
|
328317
|
+
remote: {
|
|
328318
|
+
touch_enabled: { _description: "Allow mobile devices to reach this Newmark instance on the same LAN / Tailscale network", _type: "boolean", value: true }
|
|
328319
|
+
},
|
|
328317
328320
|
models: {
|
|
328318
328321
|
providers: { _description: "LLM providers", _type: "array", value: [] },
|
|
328319
328322
|
default_model: { _description: "Default model", _type: "string", value: "" },
|
|
@@ -328860,14 +328863,18 @@ var ChatCompletionsAdapter = class {
|
|
|
328860
328863
|
...request.systemPrompt ? [{ role: CHAT_SYSTEM_ROLE, content: request.systemPrompt }] : [],
|
|
328861
328864
|
...openAIChatMessages(request.messages)
|
|
328862
328865
|
];
|
|
328866
|
+
const tools = this.serializeTools(request.tools);
|
|
328863
328867
|
const body = {
|
|
328864
328868
|
model: request.model,
|
|
328865
328869
|
messages,
|
|
328866
328870
|
temperature: request.temperature,
|
|
328867
|
-
max_tokens: request.maxOutputTokens
|
|
328868
|
-
tools: this.serializeTools(request.tools),
|
|
328869
|
-
tool_choice: "auto"
|
|
328871
|
+
max_tokens: request.maxOutputTokens
|
|
328870
328872
|
};
|
|
328873
|
+
if (tools.length) {
|
|
328874
|
+
body.tools = tools;
|
|
328875
|
+
body.tool_choice = "auto";
|
|
328876
|
+
body.parallel_tool_calls = true;
|
|
328877
|
+
}
|
|
328871
328878
|
if (request.reasoningEffort) body.reasoning_effort = request.reasoningEffort;
|
|
328872
328879
|
if (request.sessionId) body.session_id = request.sessionId;
|
|
328873
328880
|
const base2 = request.baseUrl.replace(/\/+$/, "");
|
|
@@ -329121,6 +329128,7 @@ var ResponsesAdapter = class {
|
|
|
329121
329128
|
if (tools.length) {
|
|
329122
329129
|
body.tools = tools;
|
|
329123
329130
|
body.tool_choice = "auto";
|
|
329131
|
+
body.parallel_tool_calls = true;
|
|
329124
329132
|
}
|
|
329125
329133
|
const base2 = request.baseUrl.replace(/\/+$/, "");
|
|
329126
329134
|
return {
|
|
@@ -335754,6 +335762,10 @@ var MODE_SCOPED_TOOLS = /* @__PURE__ */ new Set([
|
|
|
335754
335762
|
"task_read",
|
|
335755
335763
|
"task_create",
|
|
335756
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.
|
|
335757
335769
|
"task",
|
|
335758
335770
|
"subagent_list",
|
|
335759
335771
|
"subagent_read",
|
|
@@ -335787,6 +335799,8 @@ var PLAN_READ_ONLY_TOOLS = /* @__PURE__ */ new Set([
|
|
|
335787
335799
|
"skill",
|
|
335788
335800
|
"linked_plan",
|
|
335789
335801
|
"build_history_query",
|
|
335802
|
+
"SubAgent",
|
|
335803
|
+
"subagent_create",
|
|
335790
335804
|
"task",
|
|
335791
335805
|
"subagent_list",
|
|
335792
335806
|
"subagent_read",
|
|
@@ -335810,7 +335824,8 @@ var CONCURRENCY_SAFE_TOOLS = /* @__PURE__ */ new Set([
|
|
|
335810
335824
|
"web_fetch",
|
|
335811
335825
|
"git_status",
|
|
335812
335826
|
"file_audit",
|
|
335813
|
-
"repo_security_audit"
|
|
335827
|
+
"repo_security_audit",
|
|
335828
|
+
"SubAgent"
|
|
335814
335829
|
]);
|
|
335815
335830
|
function isConcurrencySafeTool(name50, riskLevel) {
|
|
335816
335831
|
const toolName = String(name50 || "").trim();
|
|
@@ -336978,7 +336993,7 @@ var ToolExecutor = class {
|
|
|
336978
336993
|
remote_root: { type: "string" },
|
|
336979
336994
|
remote_path: { type: "string" }
|
|
336980
336995
|
}, ["action"]),
|
|
336981
|
-
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"]),
|
|
336982
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"] } }, []),
|
|
336983
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." } }, []),
|
|
336984
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" } }, []),
|
|
@@ -337155,7 +337170,8 @@ var ToolExecutor = class {
|
|
|
337155
337170
|
return normalizeToolResult(output, { tool, workspacePath: wsPath, mode: context.mode || "" });
|
|
337156
337171
|
}
|
|
337157
337172
|
validateInvocation(tool, argsStr, _mode = "", inputSchema) {
|
|
337158
|
-
|
|
337173
|
+
const schemaTool = tool === "task" || tool === "subagent_create" ? "SubAgent" : tool;
|
|
337174
|
+
if (inputSchema === void 0 && !isNativeToolEnabled(schemaTool, this.config.nativeToolEnabled())) {
|
|
337159
337175
|
return { ok: false, error: `[tool disabled] ${tool} is disabled in Settings > Tools.` };
|
|
337160
337176
|
}
|
|
337161
337177
|
let parsed;
|
|
@@ -337177,11 +337193,11 @@ var ToolExecutor = class {
|
|
|
337177
337193
|
}, args);
|
|
337178
337194
|
return validation2.ok ? { ok: false, error: "[permission] Question is disabled by fully_autonomous option feedback." } : { ok: false, error: `[tool schema error] ${validation2.error}` };
|
|
337179
337195
|
}
|
|
337180
|
-
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 } };
|
|
337181
337197
|
if (!definition) {
|
|
337182
337198
|
return { ok: false, error: `[tool unsupported] ${tool || "(missing tool)"} is not available for the ${this.hostProfile.kind} host on ${this.hostProfile.platform}.` };
|
|
337183
337199
|
}
|
|
337184
|
-
const validation = this.argumentValidators.validate(
|
|
337200
|
+
const validation = this.argumentValidators.validate(schemaTool, definition.function.parameters, args);
|
|
337185
337201
|
return validation.ok ? { ok: true, args } : { ok: false, error: `[tool schema error] ${validation.error}` };
|
|
337186
337202
|
}
|
|
337187
337203
|
async execute(tool, argsStr, wsPath, context = {}) {
|
|
@@ -337503,7 +337519,9 @@ var ToolExecutor = class {
|
|
|
337503
337519
|
case "ssh_workspace":
|
|
337504
337520
|
return await this.sshWorkspace(args, wsPath, context.signal);
|
|
337505
337521
|
case "task":
|
|
337506
|
-
|
|
337522
|
+
case "subagent_create":
|
|
337523
|
+
case "SubAgent":
|
|
337524
|
+
return `[SubAgent] SubAgent request accepted: ${g2("name")}`;
|
|
337507
337525
|
case "subagent_send":
|
|
337508
337526
|
return `[subagent_send] Routed to Agent runtime: ${g2("name")}`;
|
|
337509
337527
|
case "subagent_read":
|
|
@@ -338676,6 +338694,7 @@ var SubagentManager = class {
|
|
|
338676
338694
|
queueMicrotask(() => this.pump());
|
|
338677
338695
|
}
|
|
338678
338696
|
bind(options) {
|
|
338697
|
+
if (options.concurrency !== void 0) this.setConcurrencyLimit(options.concurrency);
|
|
338679
338698
|
if (options.executor) this.executor = options.executor;
|
|
338680
338699
|
if (options.onChange) this.onChange = options.onChange;
|
|
338681
338700
|
if (options.persist) this.persist = options.persist;
|
|
@@ -338692,12 +338711,13 @@ var SubagentManager = class {
|
|
|
338692
338711
|
removeRootInboxListener(listener) {
|
|
338693
338712
|
this.rootInboxListeners.delete(listener);
|
|
338694
338713
|
}
|
|
338695
|
-
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 = "") {
|
|
338696
338715
|
const id = (0, import_crypto8.randomUUID)();
|
|
338697
338716
|
const shortId = id.replace(/-/g, "").slice(0, 8);
|
|
338698
338717
|
const slug = natureSlug(name50);
|
|
338699
|
-
const
|
|
338700
|
-
const
|
|
338718
|
+
const createdName = String(name50 || "SubAgent").replace(/\s+/g, " ").trim().slice(0, 160) || "SubAgent";
|
|
338719
|
+
const displayName = createdName;
|
|
338720
|
+
const qualifiedName = `${slug}--${id}`;
|
|
338701
338721
|
const stamp = now();
|
|
338702
338722
|
const record = {
|
|
338703
338723
|
id,
|
|
@@ -338705,9 +338725,11 @@ var SubagentManager = class {
|
|
|
338705
338725
|
natureSlug: slug,
|
|
338706
338726
|
displayName,
|
|
338707
338727
|
qualifiedName,
|
|
338708
|
-
name:
|
|
338728
|
+
name: createdName,
|
|
338709
338729
|
conversationId: this.conversationId,
|
|
338710
338730
|
createdByAgentId,
|
|
338731
|
+
buildRunId: String(buildRunId || "").trim() || void 0,
|
|
338732
|
+
intelligenceTier: String(intelligenceTier || "").trim() || void 0,
|
|
338711
338733
|
prompt,
|
|
338712
338734
|
model: model || "default",
|
|
338713
338735
|
inputMode: inputMode || "guide",
|
|
@@ -339030,6 +339052,20 @@ var SubagentManager = class {
|
|
|
339030
339052
|
listAll() {
|
|
339031
339053
|
return [...this.subs.values()].map(cloneRecord);
|
|
339032
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
|
+
}
|
|
339033
339069
|
pauseScheduling() {
|
|
339034
339070
|
if (this.schedulingPaused) return;
|
|
339035
339071
|
this.schedulingPaused = true;
|
|
@@ -340195,7 +340231,8 @@ function inferDomain(name50) {
|
|
|
340195
340231
|
}
|
|
340196
340232
|
if (name50 === "question") return "interaction";
|
|
340197
340233
|
if (name50 === "skill") return "skills";
|
|
340198
|
-
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";
|
|
340199
340236
|
if (/^(linked_plan|build_history_query)$/.test(name50)) return "plan";
|
|
340200
340237
|
return "general";
|
|
340201
340238
|
}
|
|
@@ -340459,7 +340496,7 @@ function resetPublicAssistantDeltaFilter(agent) {
|
|
|
340459
340496
|
function prepareAssistantToolVisibility(agent, definitions) {
|
|
340460
340497
|
const names = definitions.map(toolDefinitionName);
|
|
340461
340498
|
brokerOnlyAssistantBuffers.set(agent, {
|
|
340462
|
-
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)),
|
|
340463
340500
|
pending: [],
|
|
340464
340501
|
released: false
|
|
340465
340502
|
});
|
|
@@ -340751,7 +340788,7 @@ async function runAgentKernel(agent) {
|
|
|
340751
340788
|
const requestStartedAt = Date.now();
|
|
340752
340789
|
let firstTokenRecorded = false;
|
|
340753
340790
|
const tools = context.tools || [];
|
|
340754
|
-
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));
|
|
340755
340792
|
currentAgent.beginRouteAttempt();
|
|
340756
340793
|
try {
|
|
340757
340794
|
const currentProvider = currentAgent.engineModel();
|
|
@@ -340986,6 +341023,8 @@ function buildBuildContextBootstrap(agent, messages, options) {
|
|
|
340986
341023
|
buildConversationTaskLedger(agent),
|
|
340987
341024
|
"## Tool Awareness Bootstrap",
|
|
340988
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.",
|
|
340989
341028
|
...catalogLines.length ? catalogLines : ["- No callable tools are available for this provider turn."],
|
|
340990
341029
|
`Necessary full schemas supplied natively for this provider turn: ${activeNames.length ? activeNames.join(", ") : "(none; use tool_provision when its schema is available)"}.`,
|
|
340991
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."
|
|
@@ -341175,7 +341214,8 @@ function routeTransitionNotice(agent, previous) {
|
|
|
341175
341214
|
var TOOL_PROVISION_NAME = "tool_provision";
|
|
341176
341215
|
var INITIAL_TOOL_SCHEMA_LIMIT = 8;
|
|
341177
341216
|
var TOOL_PROVISION_BATCH_LIMIT = 8;
|
|
341178
|
-
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;
|
|
341179
341219
|
var ToolProvisionSession = class {
|
|
341180
341220
|
definitionsByName = /* @__PURE__ */ new Map();
|
|
341181
341221
|
initialNames = /* @__PURE__ */ new Set();
|
|
@@ -341199,7 +341239,7 @@ var ToolProvisionSession = class {
|
|
|
341199
341239
|
const name50 = toolDefinitionName(definition);
|
|
341200
341240
|
if (this.definitionsByName.has(name50)) this.initialNames.add(name50);
|
|
341201
341241
|
}
|
|
341202
|
-
for (const name50 of
|
|
341242
|
+
for (const name50 of ALWAYS_AVAILABLE_AGENT_TOOL_NAMES) {
|
|
341203
341243
|
if (this.definitionsByName.has(name50)) this.initialNames.add(name50);
|
|
341204
341244
|
}
|
|
341205
341245
|
for (const name50 of this.provisionedNames) {
|
|
@@ -341357,29 +341397,6 @@ function toolSurfaceIdentityForAgent(agent) {
|
|
|
341357
341397
|
optionFeedback: agent.config.getStr("agent", "option_feedback")
|
|
341358
341398
|
});
|
|
341359
341399
|
}
|
|
341360
|
-
var CAPABILITY_TO_DOMAIN = {
|
|
341361
|
-
"vcs.inspect": ["git"],
|
|
341362
|
-
"code.search": ["core"],
|
|
341363
|
-
"test.run": ["core"],
|
|
341364
|
-
"web.search": ["web"],
|
|
341365
|
-
"automation.manage": ["automation"],
|
|
341366
|
-
"flow.manage": ["flow"],
|
|
341367
|
-
"memory.manage": ["memory"],
|
|
341368
|
-
"computer.manage": ["computer"],
|
|
341369
|
-
"browser.manage": ["browser"],
|
|
341370
|
-
"terminal.manage": ["general", "core"],
|
|
341371
|
-
"github.manage": ["general"],
|
|
341372
|
-
"ssh.manage": ["general"],
|
|
341373
|
-
"skill.manage": ["skills", "general"],
|
|
341374
|
-
"plan.manage": ["plan"],
|
|
341375
|
-
"history.query": ["plan"],
|
|
341376
|
-
"media.display": ["media"],
|
|
341377
|
-
"interaction.manage": ["interaction"]
|
|
341378
|
-
};
|
|
341379
|
-
var CAPABILITY_TOOL_HINTS = {
|
|
341380
|
-
"skill.manage": ["skill", "skill_download"],
|
|
341381
|
-
"memory.manage": ["memory_lab_read", "memory_lab_query", "memory_lab_update", "memory_lab_reindex"]
|
|
341382
|
-
};
|
|
341383
341400
|
function routeToolSurfaceV2(agent, definitions, toolchain, task) {
|
|
341384
341401
|
if (!agent.shouldExposeToolInterface()) {
|
|
341385
341402
|
return {
|
|
@@ -341391,67 +341408,34 @@ function routeToolSurfaceV2(agent, definitions, toolchain, task) {
|
|
|
341391
341408
|
].join("\n")
|
|
341392
341409
|
};
|
|
341393
341410
|
}
|
|
341394
|
-
|
|
341395
|
-
const
|
|
341396
|
-
|
|
341397
|
-
|
|
341398
|
-
|
|
341399
|
-
|
|
341400
|
-
|
|
341401
|
-
|
|
341402
|
-
|
|
341403
|
-
|
|
341404
|
-
|
|
341405
|
-
|
|
341406
|
-
|
|
341407
|
-
|
|
341408
|
-
|
|
341409
|
-
|
|
341410
|
-
|
|
341411
|
-
for (const toolName of CAPABILITY_TOOL_HINTS[capabilityId] || []) toolHints.add(toolName);
|
|
341412
|
-
}
|
|
341413
|
-
const names = definitions.map(toolDefinitionName);
|
|
341414
|
-
for (const name50 of names) {
|
|
341415
|
-
if (name50 && new RegExp(`(?:^|[^A-Za-z0-9_])${name50.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:$|[^A-Za-z0-9_])`, "i").test(task)) {
|
|
341416
|
-
toolHints.add(name50);
|
|
341417
|
-
}
|
|
341418
|
-
}
|
|
341419
|
-
const selected = definitions.filter((definition) => {
|
|
341420
|
-
const name50 = toolDefinitionName(definition);
|
|
341421
|
-
const descriptor = toolchain.registry.get(name50);
|
|
341422
|
-
if (!descriptor || descriptor.riskLevel === "destructive") return false;
|
|
341423
|
-
if (toolHints.has(name50)) return true;
|
|
341424
|
-
if (!domains.has(descriptor.capabilityId.slice(4))) return false;
|
|
341425
|
-
return true;
|
|
341426
|
-
}).sort((a3, b2) => {
|
|
341427
|
-
const aHint = toolHints.has(toolDefinitionName(a3)) ? 0 : 1;
|
|
341428
|
-
const bHint = toolHints.has(toolDefinitionName(b2)) ? 0 : 1;
|
|
341429
|
-
return aHint - bHint || names.indexOf(toolDefinitionName(a3)) - names.indexOf(toolDefinitionName(b2));
|
|
341430
|
-
}).slice(0, INITIAL_TOOL_SCHEMA_LIMIT);
|
|
341431
|
-
const selectedNames = new Set(selected.map(toolDefinitionName));
|
|
341432
|
-
const core = definitions.filter((definition) => {
|
|
341433
|
-
const name50 = toolDefinitionName(definition);
|
|
341434
|
-
return SUBAGENT_CORE_TOOL_NAMES.has(name50) && !selectedNames.has(name50);
|
|
341435
|
-
});
|
|
341436
|
-
const surface = core.length ? selected.concat(core) : selected;
|
|
341437
|
-
if (surface.length === definitions.length) return { definitions, systemPromptNotice: "" };
|
|
341438
|
-
if (!selected.length) {
|
|
341439
|
-
return {
|
|
341440
|
-
definitions: surface,
|
|
341441
|
-
systemPromptNotice: [
|
|
341442
|
-
"## Tool Interface Availability",
|
|
341443
|
-
"This turn was classified as conversational, so no task-specific tool schema was preloaded.",
|
|
341444
|
-
`The ${TOOL_PROVISION_NAME} interface still exposes the complete compact capability catalog and can provision an original tool schema when the task requires it.`
|
|
341445
|
-
].join("\n")
|
|
341446
|
-
};
|
|
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);
|
|
341447
341428
|
}
|
|
341448
341429
|
return {
|
|
341449
341430
|
definitions: surface,
|
|
341450
341431
|
systemPromptNotice: [
|
|
341451
341432
|
"## Tool Interface Availability",
|
|
341452
|
-
`
|
|
341453
|
-
|
|
341454
|
-
|
|
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."
|
|
341455
341439
|
].join("\n")
|
|
341456
341440
|
};
|
|
341457
341441
|
}
|
|
@@ -341560,7 +341544,7 @@ var INLINE_TOOL_RESULT_MAX_CHARS = 24e3;
|
|
|
341560
341544
|
function spillOversizedToolResult(agent, name50, text) {
|
|
341561
341545
|
const value = String(text || "");
|
|
341562
341546
|
if (value.length <= INLINE_TOOL_RESULT_MAX_CHARS) return value;
|
|
341563
|
-
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)) {
|
|
341564
341548
|
return value;
|
|
341565
341549
|
}
|
|
341566
341550
|
const artifactId = agent.storeToolResultArtifact(name50, value);
|
|
@@ -341661,7 +341645,7 @@ async function executeNewmarkTool(agent, name50, args, inputSchema, signal) {
|
|
|
341661
341645
|
if (signal?.aborted) throw abortError4();
|
|
341662
341646
|
return result2;
|
|
341663
341647
|
};
|
|
341664
|
-
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;
|
|
341665
341649
|
if (name50 === "subagent_send") return (await agent.handleSubagentContinueEnvelope(args)).output;
|
|
341666
341650
|
if (name50 === "subagent_list") return agent.handleSubagentListEnvelope(args).output;
|
|
341667
341651
|
if (name50 === "subagent_read") return agent.handleSubagentReadEnvelope(args).output;
|
|
@@ -344813,7 +344797,7 @@ var Agent4 = class _Agent {
|
|
|
344813
344797
|
this.tools = new ToolExecutor(rootPath, this.config, this.ssh, this.workspace);
|
|
344814
344798
|
this.skills = new SkillsManager(rootPath);
|
|
344815
344799
|
this.memoryLab = new MemoryLabManager(rootPath, this.config.getStr("general", "language"));
|
|
344816
|
-
this.subagents = new SubagentManager({ rootAgentId: this.runtimeActorId });
|
|
344800
|
+
this.subagents = new SubagentManager({ rootAgentId: this.runtimeActorId, concurrency: this.subagentConcurrencyLimit() });
|
|
344817
344801
|
if (this.mode === "goal" && !this.goal) {
|
|
344818
344802
|
this.goal = new GoalStateImpl("Set your objective");
|
|
344819
344803
|
}
|
|
@@ -345412,6 +345396,7 @@ var Agent4 = class _Agent {
|
|
|
345412
345396
|
}
|
|
345413
345397
|
setIntelligence(tier, persist = false) {
|
|
345414
345398
|
this.intelligence = normalizeIntelligenceTier(tier);
|
|
345399
|
+
this.subagents?.setConcurrencyLimit(this.subagentConcurrencyLimit());
|
|
345415
345400
|
if (persist) {
|
|
345416
345401
|
this.config.set("models", "default_intelligence", this.intelligence);
|
|
345417
345402
|
this.config.save();
|
|
@@ -345616,12 +345601,17 @@ var Agent4 = class _Agent {
|
|
|
345616
345601
|
return path28.join(ws.path, "conversations", "state.json");
|
|
345617
345602
|
}
|
|
345618
345603
|
workspaceConversationStateKey(conversationId = this.activeConversationId) {
|
|
345619
|
-
|
|
345604
|
+
return this.workspaceConversationStateKeyFor(conversationId, this.workspace.current);
|
|
345605
|
+
}
|
|
345606
|
+
workspaceConversationStateKeyFor(conversationId, ws) {
|
|
345607
|
+
const prefix = this.workspaceConversationPrefixFor(ws);
|
|
345620
345608
|
if (!prefix) return null;
|
|
345621
345609
|
return `${prefix}-${this.safeConversationId(conversationId)}`;
|
|
345622
345610
|
}
|
|
345623
345611
|
workspaceConversationPrefix() {
|
|
345624
|
-
|
|
345612
|
+
return this.workspaceConversationPrefixFor(this.workspace.current);
|
|
345613
|
+
}
|
|
345614
|
+
workspaceConversationPrefixFor(ws) {
|
|
345625
345615
|
if (!ws) return null;
|
|
345626
345616
|
const supplied = String(ws.conversationStatePrefix || "").trim();
|
|
345627
345617
|
if (/^(?:internal|external)-[a-f0-9]{16}$/i.test(supplied)) return supplied.toLowerCase();
|
|
@@ -347071,8 +347061,49 @@ ${String(event.toolArgs || "")}`;
|
|
|
347071
347061
|
}
|
|
347072
347062
|
}
|
|
347073
347063
|
listConversationStates() {
|
|
347074
|
-
|
|
347075
|
-
|
|
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) || "";
|
|
347076
347107
|
const scopedEntries = Object.entries(stored.conversations || {}).filter(([key3]) => !prefix || key3.startsWith(prefix));
|
|
347077
347108
|
if (scopedEntries.some(([, value]) => !Number.isFinite(value.order))) {
|
|
347078
347109
|
const legacyOrder = [...scopedEntries].sort(([, a3], [, b2]) => {
|
|
@@ -347083,7 +347114,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
347083
347114
|
legacyOrder.forEach(([, value], index) => {
|
|
347084
347115
|
value.order = index;
|
|
347085
347116
|
});
|
|
347086
|
-
this.writeStoredConversationState(stored);
|
|
347117
|
+
this.writeStoredConversationState(stored, ws);
|
|
347087
347118
|
}
|
|
347088
347119
|
const rows = [];
|
|
347089
347120
|
for (const [key3, value] of Object.entries(stored.conversations || {})) {
|
|
@@ -347137,6 +347168,7 @@ ${String(event.toolArgs || "")}`;
|
|
|
347137
347168
|
this.subagents = sharedSubagentManager(this.subagentManagerKey(clean), {
|
|
347138
347169
|
conversationId: clean,
|
|
347139
347170
|
rootAgentId: state?.rootAgentId || this.runtimeActorId,
|
|
347171
|
+
concurrency: this.subagentConcurrencyLimit(),
|
|
347140
347172
|
state,
|
|
347141
347173
|
executor: (job) => this.runSubagentJob(job.record.id, job.prompt, job.flowName, job.reason),
|
|
347142
347174
|
persist: (subagentState) => this.persistSubagentState(clean, subagentState),
|
|
@@ -347206,15 +347238,14 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347206
347238
|
}
|
|
347207
347239
|
getConversationSnapshot(conversationId = this.activeConversationId, options = {}) {
|
|
347208
347240
|
const clean = this.safeConversationId(conversationId || "default");
|
|
347209
|
-
const
|
|
347210
|
-
const
|
|
347211
|
-
const
|
|
347212
|
-
|
|
347213
|
-
|
|
347214
|
-
|
|
347215
|
-
})();
|
|
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;
|
|
347216
347247
|
const memory = memoryKey ? this.workspaceConversations.get(memoryKey) : void 0;
|
|
347217
|
-
const stored = this.readStoredConversationState();
|
|
347248
|
+
const stored = this.readStoredConversationState(ws);
|
|
347218
347249
|
const persisted = stateKey2 && stored.conversations ? stored.conversations[stateKey2] : void 0;
|
|
347219
347250
|
const tree = persisted ? this.normalizeConversationTree(persisted) : null;
|
|
347220
347251
|
const runtimeNodeId = String(tree?.activeNodeId || "");
|
|
@@ -347234,7 +347265,7 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347234
347265
|
const continuations = this.normalizeContinuations(isActiveConversation && viewingRuntimeNode ? this.continuations : viewedNode?.continuations || persisted?.continuations || memory?.continuations);
|
|
347235
347266
|
return {
|
|
347236
347267
|
conversationId: clean,
|
|
347237
|
-
conversations: this.
|
|
347268
|
+
conversations: this.listWorkspaceConversationStates(ws),
|
|
347238
347269
|
conversationPlan: this.normalizeConversationPlan(isActiveConversation ? this.conversationPlan : persisted?.plan || memory?.plan),
|
|
347239
347270
|
linkedPlan: this.normalizeLinkedPlan(isActiveConversation ? this.linkedPlan : persisted?.linkedPlan || memory?.linkedPlan),
|
|
347240
347271
|
subagents: this.recordsForState(isActiveConversation ? this.subagents.serialize() : persisted?.subagentState || memory?.subagentState),
|
|
@@ -347244,9 +347275,9 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347244
347275
|
historyMessages: history.length,
|
|
347245
347276
|
workRuns,
|
|
347246
347277
|
continuations,
|
|
347247
|
-
modelSelection: isActiveConversation ? this.currentConversationModelSelection() : persisted?.modelSelection || memory?.modelSelection ||
|
|
347278
|
+
modelSelection: isActiveConversation ? this.currentConversationModelSelection() : persisted?.modelSelection || memory?.modelSelection || { kind: "auto" },
|
|
347248
347279
|
flowSelection: isActiveConversation ? this.currentConversationFlowSelection() : persisted?.flowSelection || memory?.flowSelection || null,
|
|
347249
|
-
inputMode: this.inputMode,
|
|
347280
|
+
inputMode: isActiveConversation ? this.inputMode : persisted?.inputMode || memory?.inputMode || "guide",
|
|
347250
347281
|
mode: isActiveConversation ? this.mode : persisted?.mode || memory?.mode || "build",
|
|
347251
347282
|
goal: isActiveConversation ? this.serializeGoal() : persisted?.goal || memory?.goal || null,
|
|
347252
347283
|
branches: this.branchGroupMetadata(tree),
|
|
@@ -347533,12 +347564,16 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347533
347564
|
if (clean === this.safeConversationId(this.activeConversationId)) this.setConversationFromStorage(clean);
|
|
347534
347565
|
return this.getConversationSnapshot(clean);
|
|
347535
347566
|
}
|
|
347536
|
-
setConversationPinned(id, pinned) {
|
|
347567
|
+
setConversationPinned(id, pinned, ws = this.workspace.current) {
|
|
347568
|
+
const targetWs = ws || this.workspace.current;
|
|
347569
|
+
if (!targetWs) return false;
|
|
347537
347570
|
const clean = this.safeConversationId(id || "default");
|
|
347538
|
-
this.
|
|
347539
|
-
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);
|
|
347540
347575
|
if (!stateKey2) return false;
|
|
347541
|
-
const stored = this.readStoredConversationState();
|
|
347576
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347542
347577
|
stored.conversations = stored.conversations || {};
|
|
347543
347578
|
const existing = stored.conversations[stateKey2];
|
|
347544
347579
|
if (!existing) return false;
|
|
@@ -347547,23 +347582,65 @@ Review this persisted peer result and summarize or continue the parent task as n
|
|
|
347547
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));
|
|
347548
347583
|
existing.order = siblingOrders.length ? Math.min(...siblingOrders) - 1 : 0;
|
|
347549
347584
|
existing.updatedAt = existing.updatedAt || (/* @__PURE__ */ new Date()).toISOString();
|
|
347550
|
-
this.writeStoredConversationState(stored);
|
|
347585
|
+
this.writeStoredConversationState(stored, targetWs);
|
|
347551
347586
|
return true;
|
|
347552
347587
|
}
|
|
347553
|
-
renameConversation(id, title) {
|
|
347588
|
+
renameConversation(id, title, ws = this.workspace.current) {
|
|
347589
|
+
const targetWs = ws || this.workspace.current;
|
|
347590
|
+
if (!targetWs) return false;
|
|
347554
347591
|
const clean = this.safeConversationId(id || "default");
|
|
347555
347592
|
const nextTitle = String(title || "").replace(/\s+/g, " ").trim().slice(0, 80);
|
|
347556
347593
|
if (!nextTitle) return false;
|
|
347557
|
-
this.
|
|
347558
|
-
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);
|
|
347559
347598
|
if (!stateKey2) return false;
|
|
347560
|
-
const stored = this.readStoredConversationState();
|
|
347599
|
+
const stored = this.readStoredConversationState(targetWs);
|
|
347561
347600
|
const existing = stored.conversations?.[stateKey2];
|
|
347562
347601
|
if (!existing) return false;
|
|
347563
347602
|
existing.title = nextTitle;
|
|
347564
|
-
this.writeStoredConversationState(stored);
|
|
347603
|
+
this.writeStoredConversationState(stored, targetWs);
|
|
347565
347604
|
return true;
|
|
347566
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
|
+
}
|
|
347567
347644
|
/**
|
|
347568
347645
|
* 首 Build 命名判定:仅当 (1) 当前对话尚无历史 Build(排除当前 run)且
|
|
347569
347646
|
* (2) 其持久化 title 仍是自动生成(含为空)时返回 true。dev-0.4.3 起不再
|
|
@@ -347674,6 +347751,34 @@ Conversation title (a few words):`;
|
|
|
347674
347751
|
this.writeStoredConversationState(stored);
|
|
347675
347752
|
return true;
|
|
347676
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
|
+
}
|
|
347677
347782
|
flushConversationState() {
|
|
347678
347783
|
this.saveWorkspaceConversationState();
|
|
347679
347784
|
}
|
|
@@ -349622,21 +349727,21 @@ ${summary}`, segment, "local-summarize", true);
|
|
|
349622
349727
|
this.saveWorkspaceConversationState(true);
|
|
349623
349728
|
return { text, hiddenUserInput: true, goalContinuation: true };
|
|
349624
349729
|
}
|
|
349625
|
-
buildSessionArchive(messages,
|
|
349730
|
+
buildSessionArchive(messages, context, archiveDir) {
|
|
349626
349731
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").replace("T", "_").replace("Z", "");
|
|
349627
349732
|
const filename = `session_${stamp}_${crypto14.randomUUID().slice(0, 8)}.md`;
|
|
349628
349733
|
let markdown = `# Newmark Session \u2014 ${stamp}
|
|
349629
349734
|
|
|
349630
349735
|
`;
|
|
349631
|
-
markdown += `**Mode**: ${mode}
|
|
349632
|
-
**Model**: ${model}
|
|
349736
|
+
markdown += `**Mode**: ${context.mode}
|
|
349737
|
+
**Model**: ${context.model}
|
|
349633
349738
|
`;
|
|
349634
349739
|
markdown += `**Messages**: ${messages.length}
|
|
349635
349740
|
|
|
349636
349741
|
---
|
|
349637
349742
|
|
|
349638
349743
|
`;
|
|
349639
|
-
if (
|
|
349744
|
+
if (context.goal?.objective) markdown += `**Goal**: ${context.goal.objective}
|
|
349640
349745
|
|
|
349641
349746
|
`;
|
|
349642
349747
|
for (const msg of messages) {
|
|
@@ -349659,12 +349764,12 @@ ${msg.content}
|
|
|
349659
349764
|
writeSessionArchive(messages, mode, model) {
|
|
349660
349765
|
const archiveDir = this.archiveDir();
|
|
349661
349766
|
fs25.mkdirSync(archiveDir, { recursive: true });
|
|
349662
|
-
const archive = this.buildSessionArchive(messages, mode, model, archiveDir);
|
|
349767
|
+
const archive = this.buildSessionArchive(messages, { mode, model, goal: this.serializeGoal() }, archiveDir);
|
|
349663
349768
|
fs25.writeFileSync(path28.join(archiveDir, archive.filename), archive.markdown, "utf-8");
|
|
349664
349769
|
return archive.filename;
|
|
349665
349770
|
}
|
|
349666
|
-
async writeSessionArchiveAsync(messages,
|
|
349667
|
-
const archive = this.buildSessionArchive(messages,
|
|
349771
|
+
async writeSessionArchiveAsync(messages, context, archiveDir = this.archiveDir()) {
|
|
349772
|
+
const archive = this.buildSessionArchive(messages, context, archiveDir);
|
|
349668
349773
|
await fs25.promises.mkdir(archiveDir, { recursive: true });
|
|
349669
349774
|
const outPath = path28.join(archiveDir, archive.filename);
|
|
349670
349775
|
const tempPath = `${outPath}.${process.pid}.${crypto14.randomUUID()}.tmp`;
|
|
@@ -349751,30 +349856,36 @@ ${msg.content}
|
|
|
349751
349856
|
* large markdown payload and manifest use promise-based filesystem I/O so
|
|
349752
349857
|
* independent workspaces can archive in parallel without freezing Electron.
|
|
349753
349858
|
*/
|
|
349754
|
-
async archiveConversationAsync(conversationId) {
|
|
349755
|
-
return await this.archiveConversationAsyncUnlocked(conversationId);
|
|
349859
|
+
async archiveConversationAsync(conversationId, ws = this.workspace.current) {
|
|
349860
|
+
return await this.archiveConversationAsyncUnlocked(conversationId, ws);
|
|
349756
349861
|
}
|
|
349757
|
-
async archiveConversationAsyncUnlocked(conversationId) {
|
|
349758
|
-
const ws = this.workspace.current;
|
|
349862
|
+
async archiveConversationAsyncUnlocked(conversationId, targetWorkspace = this.workspace.current) {
|
|
349863
|
+
const ws = targetWorkspace || this.workspace.current;
|
|
349759
349864
|
if (!ws) return null;
|
|
349760
349865
|
const clean = this.safeConversationId(conversationId || "default");
|
|
349761
|
-
const stateKey2 = this.
|
|
349866
|
+
const stateKey2 = this.workspaceConversationStateKeyFor(clean, ws);
|
|
349762
349867
|
if (!stateKey2) return null;
|
|
349763
349868
|
const memoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
349764
349869
|
const archiveDir = path28.join(ws.path, "archive");
|
|
349765
|
-
const workspacePrefix = this.
|
|
349766
|
-
const archiveMode = this.modeName();
|
|
349767
|
-
const archiveModel = this.model;
|
|
349870
|
+
const workspacePrefix = this.workspaceConversationPrefixFor(ws) || "";
|
|
349768
349871
|
const cachedStored = this.readStoredConversationState(ws);
|
|
349769
349872
|
const stored = JSON.parse(JSON.stringify(cachedStored || {}));
|
|
349770
349873
|
const persisted = stored.conversations?.[stateKey2];
|
|
349771
349874
|
if (persisted) this.normalizeConversationTree(persisted);
|
|
349772
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;
|
|
349773
349880
|
const persistedMessagesAvailable = persisted?.chatMessages !== void 0;
|
|
349774
349881
|
const sourceMessages = persisted?.chatMessages ?? memory?.chatMessages ?? [];
|
|
349775
349882
|
const sourceHistory = persistedMessagesAvailable ? persisted?.history ?? [] : memory?.history ?? persisted?.history ?? [];
|
|
349776
349883
|
const messages = this.normalizeConversationChatMessages(sourceMessages, sourceHistory);
|
|
349777
|
-
const filename = await this.writeSessionArchiveAsync(messages,
|
|
349884
|
+
const filename = await this.writeSessionArchiveAsync(messages, {
|
|
349885
|
+
mode: archiveMode,
|
|
349886
|
+
model: archiveModel,
|
|
349887
|
+
goal: archiveGoal
|
|
349888
|
+
}, archiveDir);
|
|
349778
349889
|
const archiveEntry = persisted ? JSON.parse(JSON.stringify(persisted)) : {
|
|
349779
349890
|
title: this.titleFromMessages(messages, clean),
|
|
349780
349891
|
chatMessages: messages,
|
|
@@ -349827,7 +349938,9 @@ ${msg.content}
|
|
|
349827
349938
|
this.workspaceConversations.delete(memoryKey);
|
|
349828
349939
|
const duplicateMemoryKey = `${ws.isInternal ? "internal" : "external"}:${path28.resolve(ws.path)}::conversation:${clean}`;
|
|
349829
349940
|
this.workspaceConversations.delete(duplicateMemoryKey);
|
|
349830
|
-
|
|
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")) {
|
|
349831
349944
|
this.activeConversationId = nextActiveId || "default";
|
|
349832
349945
|
this.loadWorkspaceConversationState();
|
|
349833
349946
|
}
|
|
@@ -351072,7 +351185,7 @@ ${persisted.map((guide, index) => `Guide ${index + 1}: ${guide.text}`).join("\n"
|
|
|
351072
351185
|
return `${accepted.output}
|
|
351073
351186
|
${settled?.result || settled?.error || ""}`.trim();
|
|
351074
351187
|
}
|
|
351075
|
-
async handleSubagentEnvelope(args) {
|
|
351188
|
+
async handleSubagentEnvelope(args, requireRunningBuild = false) {
|
|
351076
351189
|
try {
|
|
351077
351190
|
const params = JSON.parse(args);
|
|
351078
351191
|
const preset = this.resolveSubagentPreset(params);
|
|
@@ -351087,6 +351200,36 @@ ${settled?.result || settled?.error || ""}`.trim();
|
|
|
351087
351200
|
const requestedModel = this.normalizeSubagentModelSelection(params.model || preset?.model || inheritedModel);
|
|
351088
351201
|
const activeDeployment = this.activeDeployment();
|
|
351089
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
|
+
}
|
|
351090
351233
|
const id = this.subagents.create(
|
|
351091
351234
|
name50,
|
|
351092
351235
|
prompt,
|
|
@@ -351096,7 +351239,9 @@ ${settled?.result || settled?.error || ""}`.trim();
|
|
|
351096
351239
|
this.runtimeActorId,
|
|
351097
351240
|
peerFlow,
|
|
351098
351241
|
peerGoal,
|
|
351099
|
-
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
|
|
351100
351245
|
);
|
|
351101
351246
|
const sa = this.subagents.get(id);
|
|
351102
351247
|
if (sa && preset) {
|
|
@@ -352404,7 +352549,7 @@ ${custom}`);
|
|
|
352404
352549
|
if (this.intelligence === "ultra") {
|
|
352405
352550
|
parts.push([
|
|
352406
352551
|
"[Ultra Intelligence \u2013 Orchestrator Role]",
|
|
352407
|
-
"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.",
|
|
352408
352553
|
"Do not attempt to do all the work yourself. Use SubAgents for parallel investigation, verification, implementation, and review."
|
|
352409
352554
|
].join("\n"));
|
|
352410
352555
|
}
|
|
@@ -352510,7 +352655,7 @@ ${custom}`);
|
|
|
352510
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.",
|
|
352511
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.",
|
|
352512
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.",
|
|
352513
|
-
`- 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.`,
|
|
352514
352659
|
`- Visible output contract: assistant replies are sanitized before display to remove hidden-reasoning markers. ${visibleOutputContract}`,
|
|
352515
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."
|
|
352516
352661
|
].join("\n");
|