newmark-agent 0.3.11 → 0.4.0

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.
Files changed (69) hide show
  1. package/config.example.json +6 -0
  2. package/dist/cli-commands.d.ts +8 -0
  3. package/dist/cli-commands.js +216 -16
  4. package/dist/cli-discovery.d.ts +15 -0
  5. package/dist/cli-discovery.js +182 -0
  6. package/dist/cli-help.d.ts +2 -0
  7. package/dist/cli-help.js +25 -1
  8. package/dist/context/domain/types.d.ts +37 -0
  9. package/dist/context/services/context-orchestrator.js +2 -0
  10. package/dist/conversation-utility-host.bundle.cjs +1503 -214
  11. package/dist/conversation-utility-host.js +3 -0
  12. package/dist/core/agent.d.ts +157 -8
  13. package/dist/core/agent.js +1176 -112
  14. package/dist/core/agentKernel/agent-loop.js +29 -3
  15. package/dist/core/agentKernel/types.d.ts +7 -0
  16. package/dist/core/agentKernelRunner.d.ts +2 -0
  17. package/dist/core/agentKernelRunner.js +174 -27
  18. package/dist/core/config.d.ts +7 -2
  19. package/dist/core/config.js +24 -6
  20. package/dist/core/conversationKernel.d.ts +5 -0
  21. package/dist/core/conversationKernel.js +30 -1
  22. package/dist/core/dshCompatibility.d.ts +198 -0
  23. package/dist/core/dshCompatibility.js +600 -0
  24. package/dist/core/electronUtilityAgentClient.d.ts +4 -0
  25. package/dist/core/electronUtilityAgentClient.js +4 -0
  26. package/dist/core/electronUtilityRuntimePool.d.ts +19 -0
  27. package/dist/core/electronUtilityRuntimePool.js +76 -0
  28. package/dist/core/flow-runner.js +1 -1
  29. package/dist/core/mcpManager.d.ts +1 -0
  30. package/dist/core/mcpManager.js +100 -10
  31. package/dist/core/modelValidationStore.d.ts +4 -1
  32. package/dist/core/modelValidationStore.js +7 -1
  33. package/dist/core/subagent.d.ts +6 -0
  34. package/dist/core/subagent.js +22 -1
  35. package/dist/core/toolPolicy.d.ts +6 -0
  36. package/dist/core/toolPolicy.js +49 -1
  37. package/dist/core/types.d.ts +1 -1
  38. package/dist/core/utilityAgentProtocol.d.ts +8 -1
  39. package/dist/core/workspace.d.ts +15 -0
  40. package/dist/core/workspace.js +62 -1
  41. package/dist/core/wslAgentClient.d.ts +4 -0
  42. package/dist/core/wslAgentClient.js +4 -0
  43. package/dist/core/wslAgentProtocol.d.ts +8 -1
  44. package/dist/core/wslAgentRuntimePool.d.ts +12 -0
  45. package/dist/core/wslAgentRuntimePool.js +71 -0
  46. package/dist/launcher.js +48 -11
  47. package/dist/llm/provider.d.ts +9 -6
  48. package/dist/llm/provider.js +89 -36
  49. package/dist/main.js +326 -52
  50. package/dist/preload.js +17 -0
  51. package/dist/providers/chat-completions.adapter.js +42 -20
  52. package/dist/providers/provider-adapter.d.ts +3 -0
  53. package/dist/providers/provider-events.d.ts +7 -0
  54. package/dist/providers/provider-events.js +44 -0
  55. package/dist/providers/responses.adapter.js +1 -3
  56. package/dist/toolchain/registry/tool-registry.d.ts +13 -1
  57. package/dist/toolchain/registry/tool-registry.js +8 -0
  58. package/dist/toolchain/registry-seeder.js +51 -5
  59. package/dist/tools/index.js +11 -2
  60. package/dist/tools/nativeTools.js +5 -1
  61. package/dist/tui/src/adapters/core-runtime-adapter.js +40 -3
  62. package/dist/tui/src/app.js +47 -13
  63. package/dist/tui/src/render.js +23 -7
  64. package/dist/tui/src/state.js +61 -9
  65. package/dist/ui/index.html +2775 -284
  66. package/dist/ui/lucide-sprite.svg +26 -0
  67. package/dist/wsl-agent-host.bundle.cjs +1503 -214
  68. package/dist/wsl-agent-host.js +3 -0
  69. package/package.json +16 -5
@@ -3,6 +3,7 @@
3
3
  const readline = require("node:readline");
4
4
  const path = require("node:path");
5
5
  const { render } = require("./render");
6
+ const { targetKey } = require("./adapters/newmark-contract");
6
7
  const {
7
8
  activateMenu,
8
9
  activateMemorySelection,
@@ -89,6 +90,20 @@ function executeAction(state, action) {
89
90
  }
90
91
  }
91
92
 
93
+ function argumentValue(args, name) {
94
+ const inline = args.find((arg) => String(arg).startsWith(`${name}=`));
95
+ if (inline) return String(inline).slice(name.length + 1);
96
+ const index = args.indexOf(name);
97
+ return index >= 0 ? args[index + 1] || "" : "";
98
+ }
99
+
100
+ function resolveTuiWorkspacePath(args, options = {}) {
101
+ const explicitWorkspace = options.workspacePath || argumentValue(args, "--workspace");
102
+ if (explicitWorkspace) return explicitWorkspace;
103
+ const explicitRoot = options.root || argumentValue(args, "--root");
104
+ return explicitRoot || process.cwd();
105
+ }
106
+
92
107
  function start(options = {}) {
93
108
  const forcedTerminal = process.env.NEWMARK_FORCE_TTY === "1";
94
109
  if ((!process.stdin.isTTY || !process.stdout.isTTY) && !forcedTerminal) {
@@ -98,20 +113,16 @@ function start(options = {}) {
98
113
  }
99
114
 
100
115
  const args = process.argv.slice(2);
101
- const optionValue = (name) => {
102
- const index = args.indexOf(name);
103
- return index >= 0 ? args[index + 1] : "";
104
- };
105
116
  let adapter;
106
117
  try {
107
118
  if (args.includes("--demo") || process.env.NEWMARK_TUI_DEMO === "1") {
108
119
  adapter = require("./adapters/mock-newmark-adapter").createMockNewmarkAdapter();
109
120
  } else {
110
- const root = options.root || optionValue("--root");
111
- const workspacePath = options.workspacePath || optionValue("--workspace");
121
+ const root = options.root || argumentValue(args, "--root");
122
+ const workspacePath = resolveTuiWorkspacePath(args, { root, workspacePath: options.workspacePath });
112
123
  adapter = require("./adapters/core-runtime-adapter").createCoreRuntimeAdapter({
113
124
  root: root ? path.resolve(root) : undefined,
114
- workspacePath: workspacePath ? path.resolve(workspacePath) : process.cwd(),
125
+ workspacePath: path.resolve(workspacePath),
115
126
  desktopDist: options.desktopDist
116
127
  });
117
128
  }
@@ -185,6 +196,7 @@ function start(options = {}) {
185
196
 
186
197
  async function sendRealMessage(text) {
187
198
  const target = { ...state.target };
199
+ const key = targetKey(target);
188
200
  state.input = "";
189
201
  state.inputCursor = 0;
190
202
  state.inputMode = false;
@@ -201,13 +213,26 @@ function start(options = {}) {
201
213
  }, 250);
202
214
  }
203
215
  paint();
216
+ const previous = state.sendQueueByConversation?.get(key) || Promise.resolve();
217
+ const current = previous
218
+ .catch(() => {})
219
+ .then(() => state.adapter.sendMessage(text, target));
220
+ state.sendQueueByConversation?.set(key, current);
204
221
  try {
205
- const snapshot = await state.adapter.sendMessage(text, target);
206
- applyConversationResult(state, target, snapshot);
222
+ const snapshot = await current;
223
+ if (state.sendQueueByConversation?.get(key) === current) {
224
+ applyConversationResult(state, target, snapshot);
225
+ }
207
226
  } catch (error) {
208
- markConversationRunning(state, target, false);
209
- state.notice = `Agent error: ${error.message}`;
210
- paint();
227
+ if (state.sendQueueByConversation?.get(key) === current) {
228
+ markConversationRunning(state, target, false);
229
+ state.notice = `Agent error: ${error.message}`;
230
+ paint();
231
+ }
232
+ } finally {
233
+ if (state.sendQueueByConversation?.get(key) === current) {
234
+ state.sendQueueByConversation.delete(key);
235
+ }
211
236
  }
212
237
  }
213
238
 
@@ -439,6 +464,15 @@ function start(options = {}) {
439
464
 
440
465
  function handleKey(str, key) {
441
466
  if (key.ctrl && key.name === "c") return quit();
467
+ // A real run leaves input mode immediately after Enter. Keep Esc target-bound
468
+ // at the app boundary so users can stop that run from the content view too.
469
+ if (key.name === "escape"
470
+ && !state.overlay
471
+ && !state.memorySearchActive
472
+ && state.runningConversationKeys?.size) {
473
+ Promise.resolve(requestConversationStop(state)).finally(paint);
474
+ return;
475
+ }
442
476
  if (state.overlay === "palette") handlePalette(str, key);
443
477
  else if (state.overlay === "flow-select") handleFlowSelection(key);
444
478
  else if (state.overlay === "settings-choice") handleSettingChoice(key);
@@ -501,4 +535,4 @@ function start(options = {}) {
501
535
  paint();
502
536
  }
503
537
 
504
- module.exports = { createPaintScheduler, executeAction, start };
538
+ module.exports = { createPaintScheduler, executeAction, resolveTuiWorkspacePath, start };
@@ -1,7 +1,13 @@
1
1
  "use strict";
2
2
 
3
3
  const data = require("./data");
4
- const { activeConversationModelLabel, filteredCommands, memoryTagOptions, selectedMemoryDetail } = require("./state");
4
+ const {
5
+ activeConversationModelLabel,
6
+ filteredCommands,
7
+ memoryTagOptions,
8
+ resolveThemeAppearance,
9
+ selectedMemoryDetail
10
+ } = require("./state");
5
11
  const { SETTINGS_CATEGORIES, displaySettingValue, settingsRows } = require("./settings-schema");
6
12
 
7
13
  const ESC = "\u001b[";
@@ -94,11 +100,16 @@ function palette(state) {
94
100
  };
95
101
  const colors = state.theme === "light" ? light : dark;
96
102
  const appearance = state.settings?.personalization;
97
- if (!appearance || contrastRatio(appearance.fontColor, appearance.backgroundColor) < 4.5) {
103
+ if (!appearance) {
98
104
  return { ...colors, paint: "", final: `${ESC}0m` };
99
105
  }
100
- const [fr, fg, fb] = hexRgb(appearance.fontColor);
101
- const [br, bg, bb] = hexRgb(appearance.backgroundColor);
106
+ const resolvedAppearance = resolveThemeAppearance(
107
+ state.theme,
108
+ appearance.fontColor,
109
+ appearance.backgroundColor
110
+ );
111
+ const [fr, fg, fb] = hexRgb(resolvedAppearance.fontColor);
112
+ const [br, bg, bb] = hexRgb(resolvedAppearance.backgroundColor);
102
113
  const paint = `${ESC}38;2;${fr};${fg};${fb}m${ESC}48;2;${br};${bg};${bb}m`;
103
114
  return {
104
115
  ...colors,
@@ -951,10 +962,15 @@ function contrastRatio(foreground, background) {
951
962
 
952
963
  function personalizationPreview(state, p) {
953
964
  const appearance = state.settings.personalization;
954
- const [fr, fg, fb] = hexRgb(appearance.fontColor);
955
- const [br, bg, bb] = hexRgb(appearance.backgroundColor);
965
+ const resolvedAppearance = resolveThemeAppearance(
966
+ state.theme,
967
+ appearance.fontColor,
968
+ appearance.backgroundColor
969
+ );
970
+ const [fr, fg, fb] = hexRgb(resolvedAppearance.fontColor);
971
+ const [br, bg, bb] = hexRgb(resolvedAppearance.backgroundColor);
956
972
  const sample = `${ESC}38;2;${fr};${fg};${fb}m${ESC}48;2;${br};${bg};${bb}m Newmark Aa 中 123 ${p.reset}`;
957
- const contrast = contrastRatio(appearance.fontColor, appearance.backgroundColor);
973
+ const contrast = contrastRatio(resolvedAppearance.fontColor, resolvedAppearance.backgroundColor);
958
974
  return [
959
975
  `${p.bold}Live color preview${p.reset} ${sample}`,
960
976
  `${p.muted}Font request: ${appearance.fontFamily} · contrast ${contrast.toFixed(2)}:1 ${contrast >= 4.5 ? "PASS" : "LOW"}${p.reset}`,
@@ -5,6 +5,55 @@ const { createMockNewmarkAdapter } = require("./adapters/mock-newmark-adapter");
5
5
  const { targetKey, validateSnapshot } = require("./adapters/newmark-contract");
6
6
  const { SETTINGS_CATEGORIES, settingsRows } = require("./settings-schema");
7
7
  const INTELLIGENCE_TIERS = Object.freeze(["low", "medium", "high", "xhigh", "max", "ultra"]);
8
+ const HEX_COLOR_PATTERN = /^#[0-9a-f]{6}$/i;
9
+ const THEME_APPEARANCE_DEFAULTS = Object.freeze({
10
+ dark: Object.freeze({ fontColor: "#E6EAF2", backgroundColor: "#0A0A1A" }),
11
+ light: Object.freeze({ fontColor: "#1F2937", backgroundColor: "#F0F2F8" })
12
+ });
13
+
14
+ function normalizedTheme(value) {
15
+ return String(value || "dark").trim().toLowerCase() === "light" ? "light" : "dark";
16
+ }
17
+
18
+ function normalizedHexColor(value) {
19
+ const candidate = String(value || "").trim();
20
+ return HEX_COLOR_PATTERN.test(candidate) ? candidate.toUpperCase() : "";
21
+ }
22
+
23
+ function contrastRatio(foreground, background) {
24
+ const luminance = (value) => {
25
+ const channels = [0, 2, 4].map((offset) => Number.parseInt(value.slice(offset, offset + 2), 16) / 255)
26
+ .map((channel) => channel <= 0.03928 ? channel / 12.92 : ((channel + 0.055) / 1.055) ** 2.4);
27
+ return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
28
+ };
29
+ const foregroundLuminance = luminance(foreground.slice(1));
30
+ const backgroundLuminance = luminance(background.slice(1));
31
+ return (Math.max(foregroundLuminance, backgroundLuminance) + 0.05)
32
+ / (Math.min(foregroundLuminance, backgroundLuminance) + 0.05);
33
+ }
34
+
35
+ /**
36
+ * Resolve persisted appearance without allowing a legacy/mismatched color
37
+ * pair to disable the whole terminal canvas. Explicit colors are retained
38
+ * when readable; otherwise the closest theme-safe pair is used for rendering.
39
+ */
40
+ function resolveThemeAppearance(theme, fontColor, backgroundColor) {
41
+ const resolvedTheme = normalizedTheme(theme);
42
+ const defaults = THEME_APPEARANCE_DEFAULTS[resolvedTheme];
43
+ let foreground = normalizedHexColor(fontColor) || defaults.fontColor;
44
+ let background = normalizedHexColor(backgroundColor) || defaults.backgroundColor;
45
+ if (contrastRatio(foreground, background) < 4.5) {
46
+ if (contrastRatio(defaults.fontColor, background) >= 4.5) {
47
+ foreground = defaults.fontColor;
48
+ } else if (contrastRatio(foreground, defaults.backgroundColor) >= 4.5) {
49
+ background = defaults.backgroundColor;
50
+ } else {
51
+ foreground = defaults.fontColor;
52
+ background = defaults.backgroundColor;
53
+ }
54
+ }
55
+ return { theme: resolvedTheme, fontColor: foreground, backgroundColor: background };
56
+ }
8
57
 
9
58
  function applySnapshot(state, snapshot) {
10
59
  const valid = validateSnapshot(snapshot);
@@ -53,15 +102,15 @@ function applyThemeAppearance(state, selection) {
53
102
  : String(selection || "Dark").toLowerCase() === "system"
54
103
  ? "System"
55
104
  : "Dark";
56
- const resolvedTheme = label === "Light" ? "light" : "dark";
57
- state.theme = resolvedTheme;
105
+ const appearance = resolveThemeAppearance(label, "", "");
106
+ state.theme = appearance.theme;
58
107
  state.settings.personalization.theme = label;
59
- state.settings.personalization.fontColor = resolvedTheme === "light" ? "#1F2937" : "#E6EAF2";
60
- state.settings.personalization.backgroundColor = resolvedTheme === "light" ? "#F0F2F8" : "#0A0A1A";
108
+ state.settings.personalization.fontColor = appearance.fontColor;
109
+ state.settings.personalization.backgroundColor = appearance.backgroundColor;
61
110
  return {
62
111
  theme: label.toLowerCase(),
63
- fontColor: state.settings.personalization.fontColor,
64
- backgroundColor: state.settings.personalization.backgroundColor
112
+ fontColor: appearance.fontColor,
113
+ backgroundColor: appearance.backgroundColor
65
114
  };
66
115
  }
67
116
 
@@ -101,6 +150,7 @@ function createState(options = {}) {
101
150
  }
102
151
  return [name, workflow || { name, components: [] }];
103
152
  }));
153
+ const appearance = resolveThemeAppearance(snapshot.darkMode, snapshot.fontColor, snapshot.backgroundColor);
104
154
  return {
105
155
  adapter,
106
156
  adapterKind: adapter.kind,
@@ -135,7 +185,7 @@ function createState(options = {}) {
135
185
  flowSelectionIndex: 0,
136
186
  flowByConversation: initialFlow ? { [`${target.workspaceId}::${target.conversationId}`]: initialFlow } : {},
137
187
  currentFlow: initialFlow,
138
- theme: String(snapshot.darkMode || "dark").toLowerCase() === "light" ? "light" : "dark",
188
+ theme: appearance.theme,
139
189
  overlay: null,
140
190
  settingChoiceTab: "",
141
191
  settingChoiceKey: "",
@@ -161,6 +211,7 @@ function createState(options = {}) {
161
211
  : `Newmark core connected · ${workspaces.find((item) => item.id === target.workspaceId)?.path || target.workspaceId}`,
162
212
  busy: false,
163
213
  runningConversationKeys: new Set(),
214
+ sendQueueByConversation: new Map(),
164
215
  stopStageByConversation: new Map(),
165
216
  tick: 0,
166
217
  messages: snapshot.chatMessages.map((item) => ({ ...item })),
@@ -199,8 +250,8 @@ function createState(options = {}) {
199
250
  personalization: {
200
251
  theme: { light: "Light", system: "System", dark: "Dark" }[String(snapshot.darkMode || "dark").toLowerCase()] || "Dark",
201
252
  fontFamily: snapshot.fontFamily || "Terminal default",
202
- fontColor: snapshot.fontColor || "#E6EAF2",
203
- backgroundColor: snapshot.backgroundColor || (String(snapshot.darkMode).toLowerCase() === "light" ? "#F0F2F8" : "#0A0A1A"),
253
+ fontColor: appearance.fontColor,
254
+ backgroundColor: appearance.backgroundColor,
204
255
  glassAlpha: Number(snapshot.glassAlpha || 0.85)
205
256
  },
206
257
  runtime: {
@@ -1285,6 +1336,7 @@ module.exports = {
1285
1336
  activateMenu,
1286
1337
  activateMemorySelection,
1287
1338
  applyThemeAppearance,
1339
+ resolveThemeAppearance,
1288
1340
  applySnapshot,
1289
1341
  applyConversationResult,
1290
1342
  beginAutomationCreate,