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