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
|
@@ -35,8 +35,10 @@ function inferDomain(name) {
|
|
|
35
35
|
return 'interaction';
|
|
36
36
|
if (name === 'skill')
|
|
37
37
|
return 'skills';
|
|
38
|
-
if (name === 'task')
|
|
38
|
+
if (name === 'task' || name === 'subagent_create' || name === 'SubAgent')
|
|
39
39
|
return 'subagent';
|
|
40
|
+
if (/^task_(read|create)$/.test(name))
|
|
41
|
+
return 'plan';
|
|
40
42
|
if (/^(linked_plan|build_history_query)$/.test(name))
|
|
41
43
|
return 'plan';
|
|
42
44
|
return 'general';
|
package/dist/tools/index.js
CHANGED
|
@@ -379,7 +379,7 @@ class ToolExecutor {
|
|
|
379
379
|
remote_root: { type: 'string' },
|
|
380
380
|
remote_path: { type: 'string' },
|
|
381
381
|
}, ['action']),
|
|
382
|
-
t('
|
|
382
|
+
t('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']),
|
|
383
383
|
t('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'] } }, []),
|
|
384
384
|
t('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.' } }, []),
|
|
385
385
|
t('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' } }, []),
|
|
@@ -559,7 +559,11 @@ class ToolExecutor {
|
|
|
559
559
|
return (0, compat_1.normalizeToolResult)(output, { tool, workspacePath: wsPath, mode: context.mode || '' });
|
|
560
560
|
}
|
|
561
561
|
validateInvocation(tool, argsStr, _mode = '', inputSchema) {
|
|
562
|
-
|
|
562
|
+
// Persisted work history may replay the old create aliases. They share the
|
|
563
|
+
// public SubAgent schema and enablement, but are never advertised back to
|
|
564
|
+
// the model as independent tools.
|
|
565
|
+
const schemaTool = tool === 'task' || tool === 'subagent_create' ? 'SubAgent' : tool;
|
|
566
|
+
if (inputSchema === undefined && !(0, nativeTools_1.isNativeToolEnabled)(schemaTool, this.config.nativeToolEnabled())) {
|
|
563
567
|
return { ok: false, error: `[tool disabled] ${tool} is disabled in Settings > Tools.` };
|
|
564
568
|
}
|
|
565
569
|
let parsed;
|
|
@@ -591,12 +595,12 @@ class ToolExecutor {
|
|
|
591
595
|
// catalogs may advertise a narrower enum, but a structurally valid
|
|
592
596
|
// hidden call must reach evaluateToolPolicy so it is classified as a
|
|
593
597
|
// policy denial rather than a misleading schema failure.
|
|
594
|
-
? this.definitions().find(candidate => candidate.function?.name ===
|
|
598
|
+
? this.definitions().find(candidate => candidate.function?.name === schemaTool)
|
|
595
599
|
: { function: { name: tool, parameters: inputSchema } };
|
|
596
600
|
if (!definition) {
|
|
597
601
|
return { ok: false, error: `[tool unsupported] ${tool || '(missing tool)'} is not available for the ${this.hostProfile.kind} host on ${this.hostProfile.platform}.` };
|
|
598
602
|
}
|
|
599
|
-
const validation = this.argumentValidators.validate(
|
|
603
|
+
const validation = this.argumentValidators.validate(schemaTool, definition.function.parameters, args);
|
|
600
604
|
return validation.ok ? { ok: true, args } : { ok: false, error: `[tool schema error] ${validation.error}` };
|
|
601
605
|
}
|
|
602
606
|
async execute(tool, argsStr, wsPath, context = {}) {
|
|
@@ -918,7 +922,9 @@ class ToolExecutor {
|
|
|
918
922
|
});
|
|
919
923
|
}
|
|
920
924
|
case 'ssh_workspace': return await this.sshWorkspace(args, wsPath, context.signal);
|
|
921
|
-
case 'task':
|
|
925
|
+
case 'task':
|
|
926
|
+
case 'subagent_create':
|
|
927
|
+
case 'SubAgent': return `[SubAgent] SubAgent request accepted: ${g('name')}`;
|
|
922
928
|
case 'subagent_send': return `[subagent_send] Routed to Agent runtime: ${g('name')}`;
|
|
923
929
|
case 'subagent_read': return `[subagent_read] Routed to Agent runtime: ${g('name') || g('id')}`;
|
|
924
930
|
case 'subagent_result': return `[subagent_result] Routed to Agent runtime: ${g('name')}`;
|
|
@@ -33,7 +33,7 @@ exports.NATIVE_TOOL_CATALOG = [
|
|
|
33
33
|
{ 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' },
|
|
34
34
|
{ name: 'terminal_takeover', label: 'Terminal takeover', description: 'Maintain a persistent Agent-controlled shell session.', category: 'desktop', defaultEnabled: true },
|
|
35
35
|
{ name: 'ssh_workspace', label: 'OpenSSH workspace', description: 'Manage native OpenSSH connections and link remote workspaces by PC_Hash.', category: 'ssh', defaultEnabled: true },
|
|
36
|
-
{ name: '
|
|
36
|
+
{ name: 'SubAgent', label: 'SubAgent', description: 'Create one real same-conversation peer SubAgent.', category: 'agent', defaultEnabled: true, protected: true, availability: 'mode-scoped' },
|
|
37
37
|
{ name: 'subagent_list', label: 'Subagent list', description: 'List same-conversation peer agents.', category: 'agent', defaultEnabled: true, protected: true, availability: 'mode-scoped' },
|
|
38
38
|
{ 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' },
|
|
39
39
|
{ name: 'subagent_send', label: 'Subagent send', description: 'Persist a message to a peer mailbox.', category: 'agent', defaultEnabled: true, protected: true, availability: 'mode-scoped' },
|
|
@@ -154,6 +154,7 @@ function createCoreRuntimeAdapter(options = {}) {
|
|
|
154
154
|
const { AutomationManager } = require(path.join(desktopDist, "core", "automation.js"));
|
|
155
155
|
const { FlowEngine } = require(path.join(desktopDist, "core", "flow.js"));
|
|
156
156
|
const installUpdate = require(path.join(desktopDist, "core", "installUpdate.js"));
|
|
157
|
+
const mobilePairing = require(path.join(desktopDist, "core", "mobilePairing.js"));
|
|
157
158
|
const root = path.resolve(options.root || path.join(os.homedir(), ".Newmark"));
|
|
158
159
|
const workspacePath = safeWorkspacePath(root, options.workspacePath || process.cwd());
|
|
159
160
|
ensureRuntimeRoot(root, configModule);
|
|
@@ -211,7 +212,8 @@ function createCoreRuntimeAdapter(options = {}) {
|
|
|
211
212
|
defaultTerminalShell: agent.config.getStr("terminal", "default_shell") || (process.platform === "win32" ? "powershell" : "bash"),
|
|
212
213
|
status: agent.status,
|
|
213
214
|
connected: true,
|
|
214
|
-
runtimeRoot: root
|
|
215
|
+
runtimeRoot: root,
|
|
216
|
+
remoteTouchEnabled: agent.config.getBool('remote', 'touch_enabled')
|
|
215
217
|
});
|
|
216
218
|
|
|
217
219
|
const snapshotFor = (requested = currentTarget()) => {
|
|
@@ -473,6 +475,20 @@ function createCoreRuntimeAdapter(options = {}) {
|
|
|
473
475
|
updateVersion() {
|
|
474
476
|
return { version: installUpdate.currentAppVersion(), root };
|
|
475
477
|
},
|
|
478
|
+
async pairingQr() {
|
|
479
|
+
const qr = await mobilePairing.pairingQrAscii(root);
|
|
480
|
+
return {
|
|
481
|
+
ascii: qr.ascii,
|
|
482
|
+
url: qr.session.url,
|
|
483
|
+
pairingId: qr.session.pairingId,
|
|
484
|
+
expiresAt: qr.session.expiresAt,
|
|
485
|
+
tokenFile: mobilePairing.pairingTokenPath(root),
|
|
486
|
+
tailscaleIpv4: mobilePairing.tailscaleIpv4(),
|
|
487
|
+
};
|
|
488
|
+
},
|
|
489
|
+
pairingStatus() {
|
|
490
|
+
return mobilePairing.pairingStatus(root);
|
|
491
|
+
},
|
|
476
492
|
updateCheckGithub(input = {}) {
|
|
477
493
|
return installUpdate.checkGitHubUpdate(input.repo, input.tag, input.asset, input.token);
|
|
478
494
|
},
|
package/dist/tui/src/app.js
CHANGED
|
@@ -93,6 +93,46 @@ function executeAction(state, action) {
|
|
|
93
93
|
const appearance = applyThemeAppearance(state, state.theme === "dark" ? "Light" : "Dark");
|
|
94
94
|
state.adapter.saveConfig(appearance);
|
|
95
95
|
state.notice = `${state.theme === "dark" ? "Dark" : "Light"} terminal theme`;
|
|
96
|
+
} else if (action === "pair-mobile") {
|
|
97
|
+
state.overlay = "pair";
|
|
98
|
+
state.pairingQrLines = ["Loading pairing QR…"];
|
|
99
|
+
state.pairingUrl = "";
|
|
100
|
+
state.pairingTokenFile = "";
|
|
101
|
+
if (typeof state.requestPaint === "function") state.requestPaint();
|
|
102
|
+
if (typeof state.adapter.pairingQr !== "function") {
|
|
103
|
+
state.pairingQrLines = ["Pairing QR is unavailable in this adapter."];
|
|
104
|
+
if (typeof state.requestPaint === "function") state.requestPaint();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
Promise.resolve(state.adapter.pairingQr())
|
|
108
|
+
.then((pairing) => {
|
|
109
|
+
state.pairingQrLines = String(pairing?.ascii || "").split(/\r?\n/);
|
|
110
|
+
state.pairingUrl = String(pairing?.url || "");
|
|
111
|
+
state.pairingTokenFile = String(pairing?.tokenFile || "");
|
|
112
|
+
if (typeof state.requestPaint === "function") state.requestPaint();
|
|
113
|
+
if (typeof state.adapter.pairingStatus === "function") {
|
|
114
|
+
if (state._pairingPoll) clearInterval(state._pairingPoll);
|
|
115
|
+
state._pairingPoll = setInterval(() => {
|
|
116
|
+
const status = state.adapter.pairingStatus();
|
|
117
|
+
if (!status) return;
|
|
118
|
+
if (status.confirmed) {
|
|
119
|
+
clearInterval(state._pairingPoll);
|
|
120
|
+
state.overlay = null;
|
|
121
|
+
state.notice = "Mobile device connected";
|
|
122
|
+
if (typeof state.requestPaint === "function") state.requestPaint();
|
|
123
|
+
} else if (status.expired || !status.active) {
|
|
124
|
+
clearInterval(state._pairingPoll);
|
|
125
|
+
state.overlay = null;
|
|
126
|
+
state.notice = "Pairing window expired";
|
|
127
|
+
if (typeof state.requestPaint === "function") state.requestPaint();
|
|
128
|
+
}
|
|
129
|
+
}, 1000);
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
.catch((error) => {
|
|
133
|
+
state.pairingQrLines = [`Pairing failed: ${error?.message || error}`];
|
|
134
|
+
if (typeof state.requestPaint === "function") state.requestPaint();
|
|
135
|
+
});
|
|
96
136
|
} else if (action === "help") {
|
|
97
137
|
state.overlay = "help";
|
|
98
138
|
}
|
|
@@ -166,6 +206,7 @@ function start(options = {}) {
|
|
|
166
206
|
return;
|
|
167
207
|
}
|
|
168
208
|
const state = createState({ adapter });
|
|
209
|
+
state.requestPaint = () => paint();
|
|
169
210
|
let timer = null;
|
|
170
211
|
let animationTimer = null;
|
|
171
212
|
let closing = false;
|
package/dist/tui/src/data.js
CHANGED
|
@@ -315,6 +315,7 @@ const commands = [
|
|
|
315
315
|
{ label: "Open Automations", hint: "", action: "view:automation" },
|
|
316
316
|
{ label: "Open WorkFlow", hint: "", action: "view:workflow" },
|
|
317
317
|
{ label: "Open Settings", hint: "", action: "view:settings" },
|
|
318
|
+
{ label: "Show mobile pairing QR", hint: "", action: "pair-mobile" },
|
|
318
319
|
{ label: "New conversation", hint: "N", action: "new-chat" },
|
|
319
320
|
{ label: "Toggle theme", hint: "T", action: "theme" },
|
|
320
321
|
{ label: "Keyboard shortcuts", hint: "?", action: "help" }
|
package/dist/tui/src/render.js
CHANGED
|
@@ -1154,6 +1154,17 @@ function overlayLines(state, width, p) {
|
|
|
1154
1154
|
`${p.cyan}Press Esc or ? to close${p.reset}`
|
|
1155
1155
|
], Math.min(78, width - 4), p, "Keyboard shortcuts");
|
|
1156
1156
|
}
|
|
1157
|
+
if (state.overlay === "pair") {
|
|
1158
|
+
const qrLines = Array.isArray(state.pairingQrLines) ? state.pairingQrLines : [];
|
|
1159
|
+
const rows = [
|
|
1160
|
+
...qrLines,
|
|
1161
|
+
"",
|
|
1162
|
+
...(state.pairingUrl ? [`${p.muted}${state.pairingUrl}${p.reset}`] : []),
|
|
1163
|
+
...(state.pairingTokenFile ? [`${p.muted}Token: ${state.pairingTokenFile}${p.reset}`] : []),
|
|
1164
|
+
`${p.cyan}Scan with the Newmark mobile app · Esc close${p.reset}`
|
|
1165
|
+
];
|
|
1166
|
+
return card(rows, Math.min(88, width - 4), p, "Pair mobile device");
|
|
1167
|
+
}
|
|
1157
1168
|
if (state.overlay === "palette") {
|
|
1158
1169
|
const commands = filteredCommands(state);
|
|
1159
1170
|
const paletteViewport = Math.min(7, Math.max(1, commands.length));
|
|
@@ -23,7 +23,9 @@ function settingsRows(state, tab = state.settingsTab) {
|
|
|
23
23
|
{ key: "dialogStyle", label: "Conversation style", value: s.general.dialogStyle, choices: ["Formal", "Friendly"], save: ["config", "dialogStyle"] },
|
|
24
24
|
{ key: "feedbackLevel", label: "Option feedback", value: s.general.feedbackLevel, choices: ["Default", "Ask more", "Ask less", "Autonomous"], save: ["config", "feedbackLevel"] },
|
|
25
25
|
{ key: "closeBehavior", label: "Close behavior", value: s.general.closeBehavior, choices: ["Close app", "Minimize to tray"], save: ["setting", "general", "close_behavior"] },
|
|
26
|
-
{ key: "expandTools", label: "Expand tool usage", value: s.general.expandTools, choices: [true, false], save: ["setting", "general", "expand_tools"] }
|
|
26
|
+
{ key: "expandTools", label: "Expand tool usage", value: s.general.expandTools, choices: [true, false], save: ["setting", "general", "expand_tools"] },
|
|
27
|
+
{ key: "remoteTouch", label: "Remote behavior · Mobile remote-touch", value: s.general.remoteTouch, choices: [true, false], save: ["setting", "remote", "touch_enabled"] },
|
|
28
|
+
{ key: "remotePair", label: "Remote behavior · Start connection", value: "Open QR", choices: [], action: "pair-mobile" }
|
|
27
29
|
];
|
|
28
30
|
if (tab === "personalization") return [
|
|
29
31
|
{ key: "theme", label: "Theme", value: s.personalization.theme, choices: ["Dark", "Light", "System"], save: ["config", "theme"] },
|
package/dist/tui/src/state.js
CHANGED
|
@@ -253,7 +253,8 @@ function createState(options = {}) {
|
|
|
253
253
|
dialogStyle: snapshot.dialogStyle === "friendly" ? "Friendly" : "Formal",
|
|
254
254
|
feedbackLevel: { ask_more: "Ask more", ask_less: "Ask less", fully_autonomous: "Autonomous" }[snapshot.feedback] || "Default",
|
|
255
255
|
closeBehavior: snapshot.closeBehavior === "minimize" ? "Minimize to tray" : "Close app",
|
|
256
|
-
expandTools: snapshot.expandToolsDefault !== false
|
|
256
|
+
expandTools: snapshot.expandToolsDefault !== false,
|
|
257
|
+
remoteTouch: snapshot.remoteTouchEnabled !== false
|
|
257
258
|
},
|
|
258
259
|
personalization: {
|
|
259
260
|
theme: { light: "Light", system: "System", dark: "Dark" }[String(snapshot.darkMode || "dark").toLowerCase()] || "Dark",
|
|
@@ -1071,6 +1072,25 @@ function toggleSelected(state) {
|
|
|
1071
1072
|
}
|
|
1072
1073
|
const row = settingsRows(state)[state.selected];
|
|
1073
1074
|
if (!row) return;
|
|
1075
|
+
if (row.action === "pair-mobile") {
|
|
1076
|
+
state.overlay = "pair";
|
|
1077
|
+
state.pairingQrLines = ["Loading pairing QR…"];
|
|
1078
|
+
state.pairingUrl = "";
|
|
1079
|
+
state.pairingTokenFile = "";
|
|
1080
|
+
if (typeof state.adapter.pairingQr !== "function") {
|
|
1081
|
+
state.pairingQrLines = ["Pairing QR is unavailable in this adapter."];
|
|
1082
|
+
return false;
|
|
1083
|
+
}
|
|
1084
|
+
return Promise.resolve(state.adapter.pairingQr()).then((pairing) => {
|
|
1085
|
+
state.pairingQrLines = String(pairing?.ascii || "").split(/\r?\n/);
|
|
1086
|
+
state.pairingUrl = String(pairing?.url || "");
|
|
1087
|
+
state.pairingTokenFile = String(pairing?.tokenFile || "");
|
|
1088
|
+
return true;
|
|
1089
|
+
}).catch((error) => {
|
|
1090
|
+
state.pairingQrLines = [`Pairing failed: ${error?.message || error}`];
|
|
1091
|
+
return false;
|
|
1092
|
+
});
|
|
1093
|
+
}
|
|
1074
1094
|
if (state.settingsTab === "general" && row.key === "inputBehavior") {
|
|
1075
1095
|
const current = row.choices.findIndex((value) => value === row.value);
|
|
1076
1096
|
state.settingChoiceTab = state.settingsTab;
|