newmark-agent 0.3.11 → 0.3.12
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/config.example.json +6 -0
- package/dist/cli-commands.d.ts +7 -0
- package/dist/cli-commands.js +206 -15
- package/dist/cli-discovery.d.ts +15 -0
- package/dist/cli-discovery.js +182 -0
- package/dist/cli-help.d.ts +2 -0
- package/dist/cli-help.js +25 -1
- package/dist/conversation-utility-host.bundle.cjs +357 -84
- package/dist/core/agent.d.ts +18 -3
- package/dist/core/agent.js +214 -30
- package/dist/core/agentKernelRunner.js +53 -8
- package/dist/core/config.d.ts +7 -2
- package/dist/core/config.js +24 -6
- package/dist/core/conversationKernel.js +1 -1
- package/dist/core/electronUtilityRuntimePool.d.ts +11 -0
- package/dist/core/electronUtilityRuntimePool.js +62 -0
- package/dist/core/flow-runner.js +1 -1
- package/dist/core/modelValidationStore.d.ts +4 -1
- package/dist/core/modelValidationStore.js +7 -1
- package/dist/core/workspace.d.ts +6 -0
- package/dist/core/workspace.js +14 -0
- package/dist/core/wslAgentRuntimePool.d.ts +4 -0
- package/dist/core/wslAgentRuntimePool.js +56 -0
- package/dist/launcher.js +40 -11
- package/dist/llm/provider.d.ts +8 -5
- package/dist/llm/provider.js +85 -33
- package/dist/main.js +167 -45
- package/dist/preload.js +6 -0
- package/dist/providers/chat-completions.adapter.js +1 -5
- package/dist/providers/provider-events.d.ts +7 -0
- package/dist/providers/provider-events.js +44 -0
- package/dist/providers/responses.adapter.js +1 -3
- package/dist/tui/src/adapters/core-runtime-adapter.js +40 -3
- package/dist/tui/src/app.js +47 -13
- package/dist/tui/src/render.js +23 -7
- package/dist/tui/src/state.js +61 -9
- package/dist/ui/index.html +250 -84
- package/dist/wsl-agent-host.bundle.cjs +357 -84
- package/package.json +14 -5
package/dist/core/config.js
CHANGED
|
@@ -64,8 +64,10 @@ class ConfigManager {
|
|
|
64
64
|
rootPath;
|
|
65
65
|
config;
|
|
66
66
|
workspaceOverrides;
|
|
67
|
-
|
|
67
|
+
readOnly;
|
|
68
|
+
constructor(rootPath, options = {}) {
|
|
68
69
|
this.rootPath = rootPath;
|
|
70
|
+
this.readOnly = options.readOnly === true;
|
|
69
71
|
this.workspaceOverrides = new Map();
|
|
70
72
|
this.config = this.load();
|
|
71
73
|
}
|
|
@@ -80,6 +82,8 @@ class ConfigManager {
|
|
|
80
82
|
const raw = JSON.parse(readJsonText(cp));
|
|
81
83
|
const normalized = normalizeConfigShape(raw, true);
|
|
82
84
|
if (isCorruptConfig(raw, normalized)) {
|
|
85
|
+
if (this.readOnly)
|
|
86
|
+
return defaultConfig();
|
|
83
87
|
this.backupConfig(cp, 'invalid-shape');
|
|
84
88
|
return this.writeRecoveredConfig(cp);
|
|
85
89
|
}
|
|
@@ -87,7 +91,8 @@ class ConfigManager {
|
|
|
87
91
|
// Provider ids are routing identities, so legacy/malformed catalogs must
|
|
88
92
|
// not wait for an unrelated settings save before becoming collision-safe.
|
|
89
93
|
try {
|
|
90
|
-
|
|
94
|
+
if (!this.readOnly)
|
|
95
|
+
fs.writeFileSync(cp, JSON.stringify(normalized, null, 2), 'utf-8');
|
|
91
96
|
}
|
|
92
97
|
catch {
|
|
93
98
|
// A read-only root may still be used for this process. The normalized
|
|
@@ -97,6 +102,8 @@ class ConfigManager {
|
|
|
97
102
|
return normalized;
|
|
98
103
|
}
|
|
99
104
|
catch {
|
|
105
|
+
if (this.readOnly)
|
|
106
|
+
return defaultConfig();
|
|
100
107
|
this.backupConfig(cp, 'invalid-json');
|
|
101
108
|
return this.writeRecoveredConfig(cp);
|
|
102
109
|
}
|
|
@@ -146,10 +153,14 @@ class ConfigManager {
|
|
|
146
153
|
this.config[section][key] = { value: normalizedValue };
|
|
147
154
|
}
|
|
148
155
|
save() {
|
|
156
|
+
if (this.readOnly)
|
|
157
|
+
return;
|
|
149
158
|
const j = JSON.stringify(this.config, null, 2);
|
|
150
159
|
fs.writeFileSync(path.join(this.rootPath, 'config.json'), j, 'utf-8');
|
|
151
160
|
}
|
|
152
161
|
saveTo(targetPath) {
|
|
162
|
+
if (this.readOnly)
|
|
163
|
+
return;
|
|
153
164
|
const j = JSON.stringify(this.config, null, 2);
|
|
154
165
|
fs.writeFileSync(targetPath, j, 'utf-8');
|
|
155
166
|
}
|
|
@@ -393,12 +404,16 @@ class ConfigManager {
|
|
|
393
404
|
return providers;
|
|
394
405
|
}
|
|
395
406
|
writeRecoveredConfig(configPath) {
|
|
407
|
+
if (this.readOnly)
|
|
408
|
+
return defaultConfig();
|
|
396
409
|
const config = loadExampleConfig();
|
|
397
410
|
fs.mkdirSync(path.dirname(configPath), { recursive: true });
|
|
398
411
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
399
412
|
return config;
|
|
400
413
|
}
|
|
401
414
|
backupConfig(configPath, reason) {
|
|
415
|
+
if (this.readOnly)
|
|
416
|
+
return;
|
|
402
417
|
try {
|
|
403
418
|
if (!fs.existsSync(configPath))
|
|
404
419
|
return;
|
|
@@ -412,13 +427,13 @@ class ConfigManager {
|
|
|
412
427
|
}
|
|
413
428
|
}
|
|
414
429
|
exports.ConfigManager = ConfigManager;
|
|
415
|
-
function ensureRootConfig(rootPath) {
|
|
430
|
+
function ensureRootConfig(rootPath, options = {}) {
|
|
416
431
|
const configPath = path.join(rootPath, 'config.json');
|
|
417
432
|
if (fs.existsSync(configPath)) {
|
|
418
|
-
new ConfigManager(rootPath);
|
|
433
|
+
new ConfigManager(rootPath, options);
|
|
419
434
|
return;
|
|
420
435
|
}
|
|
421
|
-
new ConfigManager(rootPath).save();
|
|
436
|
+
new ConfigManager(rootPath, options).save();
|
|
422
437
|
}
|
|
423
438
|
function normalizeConfigShape(raw, withDefaults) {
|
|
424
439
|
const base = withDefaults ? defaultConfig() : {};
|
|
@@ -827,7 +842,10 @@ function defaultConfig() {
|
|
|
827
842
|
general: {
|
|
828
843
|
tone: { _description: "Conversation style", _type: "choice", _values: ["strict_simple", "casual_friendly"], value: "strict_simple" },
|
|
829
844
|
language: { _description: "Default language", _type: "choice", _values: ["en", "zh", "auto"], value: "auto" },
|
|
830
|
-
|
|
845
|
+
// A first-run desktop window must have a deterministic close/exit
|
|
846
|
+
// contract. Users who explicitly choose minimize-to-tray keep that
|
|
847
|
+
// choice, but a fresh install must not hide the process on OS close.
|
|
848
|
+
close_behavior: { _description: "Close behavior", _type: "choice", _values: ["minimize", "exit"], value: "exit" },
|
|
831
849
|
default_input: { _description: "Default input mode", _type: "choice", _values: ["guide", "next"], value: "guide" },
|
|
832
850
|
auto_archive_on_close: { _description: "Auto archive on close", _type: "boolean", value: true },
|
|
833
851
|
},
|
|
@@ -499,7 +499,7 @@ class ConversationKernel {
|
|
|
499
499
|
stopped = true;
|
|
500
500
|
}
|
|
501
501
|
else {
|
|
502
|
-
runtime.runner.finishConversationWorkRun(runId, 'error');
|
|
502
|
+
runtime.runner.finishConversationWorkRun(runId, 'error', undefined, error instanceof Error ? error.message : String(error));
|
|
503
503
|
throw error;
|
|
504
504
|
}
|
|
505
505
|
}
|
|
@@ -55,6 +55,7 @@ export declare class ElectronUtilityRuntimePool {
|
|
|
55
55
|
private restarting;
|
|
56
56
|
private quarantined;
|
|
57
57
|
private disposing;
|
|
58
|
+
private forceStopPromises;
|
|
58
59
|
private capacityTail;
|
|
59
60
|
private accessSequence;
|
|
60
61
|
constructor(root: string, hostScript: string, createClient?: ElectronTargetRuntimeClientFactory, options?: ElectronUtilityRuntimePoolOptions);
|
|
@@ -86,6 +87,16 @@ export declare class ElectronUtilityRuntimePool {
|
|
|
86
87
|
hasActiveWorkspace(target: ConversationRuntimeTarget): Promise<boolean>;
|
|
87
88
|
stopWorkspace(target: ConversationRuntimeTarget): Promise<void>;
|
|
88
89
|
stopTarget(target: ConversationRuntimeTarget): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Immediately terminate a target runtime for destructive lifecycle actions.
|
|
92
|
+
* Archive is deliberately stronger than the user-facing two-click Stop
|
|
93
|
+
* contract: the target may be running, already stopping, or holding an
|
|
94
|
+
* active prompt lease. Give the kernel only the short checkpoint window
|
|
95
|
+
* owned by forceTerminateEntry, then hard-stop and evict the client so no
|
|
96
|
+
* delayed runtime event can keep the archived target resident.
|
|
97
|
+
*/
|
|
98
|
+
forceStopTarget(target: ConversationRuntimeTarget): Promise<void>;
|
|
99
|
+
private forceStopTargetInternal;
|
|
89
100
|
stopAll(): Promise<void>;
|
|
90
101
|
private throwStopFailures;
|
|
91
102
|
private stopEntry;
|
|
@@ -16,6 +16,7 @@ class ElectronUtilityRuntimePool {
|
|
|
16
16
|
restarting = new Set();
|
|
17
17
|
quarantined = new Map();
|
|
18
18
|
disposing = new Set();
|
|
19
|
+
forceStopPromises = new Map();
|
|
19
20
|
capacityTail = Promise.resolve();
|
|
20
21
|
accessSequence = 0;
|
|
21
22
|
constructor(root, hostScript, createClient = target => new electronUtilityAgentClient_1.ElectronUtilityAgentClient(root, hostScript, target), options = {}) {
|
|
@@ -342,6 +343,67 @@ class ElectronUtilityRuntimePool {
|
|
|
342
343
|
if (entry)
|
|
343
344
|
await this.stopEntry(entry);
|
|
344
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* Immediately terminate a target runtime for destructive lifecycle actions.
|
|
348
|
+
* Archive is deliberately stronger than the user-facing two-click Stop
|
|
349
|
+
* contract: the target may be running, already stopping, or holding an
|
|
350
|
+
* active prompt lease. Give the kernel only the short checkpoint window
|
|
351
|
+
* owned by forceTerminateEntry, then hard-stop and evict the client so no
|
|
352
|
+
* delayed runtime event can keep the archived target resident.
|
|
353
|
+
*/
|
|
354
|
+
async forceStopTarget(target) {
|
|
355
|
+
const normalized = (0, conversationTarget_1.normalizeConversationTarget)(target);
|
|
356
|
+
const existing = this.forceStopPromises.get(normalized.runtimeKey);
|
|
357
|
+
if (existing)
|
|
358
|
+
return existing;
|
|
359
|
+
const operation = this.forceStopTargetInternal(normalized);
|
|
360
|
+
this.forceStopPromises.set(normalized.runtimeKey, operation);
|
|
361
|
+
try {
|
|
362
|
+
await operation;
|
|
363
|
+
}
|
|
364
|
+
finally {
|
|
365
|
+
if (this.forceStopPromises.get(normalized.runtimeKey) === operation) {
|
|
366
|
+
this.forceStopPromises.delete(normalized.runtimeKey);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async forceStopTargetInternal(target) {
|
|
371
|
+
let entry;
|
|
372
|
+
await this.serializeCapacity(async () => {
|
|
373
|
+
entry = this.entries.get(target.runtimeKey);
|
|
374
|
+
if (entry)
|
|
375
|
+
this.disposing.add(target.runtimeKey);
|
|
376
|
+
});
|
|
377
|
+
if (!entry)
|
|
378
|
+
return;
|
|
379
|
+
const intent = entry.stopIntent || {
|
|
380
|
+
runId: entry.lastRunId,
|
|
381
|
+
generation: entry.lastGeneration,
|
|
382
|
+
checkpointed: false,
|
|
383
|
+
forcePromise: null,
|
|
384
|
+
};
|
|
385
|
+
if (!entry.stopIntent)
|
|
386
|
+
entry.stopIntent = intent;
|
|
387
|
+
try {
|
|
388
|
+
await this.forceTerminateEntry(entry, intent);
|
|
389
|
+
// forceStop is expected to disconnect the utility child. If a client
|
|
390
|
+
// reports a stale connected flag, retry the hard boundary once before
|
|
391
|
+
// declaring archive unsafe.
|
|
392
|
+
if (entry.client.status().connected)
|
|
393
|
+
await entry.client.forceStop();
|
|
394
|
+
if (entry.client.status().connected) {
|
|
395
|
+
throw new Error(`Electron utility runtime ${target.runtimeKey} remained connected after force stop`);
|
|
396
|
+
}
|
|
397
|
+
if (this.entries.get(target.runtimeKey) === entry) {
|
|
398
|
+
this.entries.delete(target.runtimeKey);
|
|
399
|
+
entry.unsubscribe();
|
|
400
|
+
entry.client.setHostToolHandler(null);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
finally {
|
|
404
|
+
this.disposing.delete(target.runtimeKey);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
345
407
|
async stopAll() {
|
|
346
408
|
const targets = Array.from(this.entries.values(), entry => entry.target);
|
|
347
409
|
const results = await Promise.allSettled(targets.map(target => this.stopTarget(target)));
|
package/dist/core/flow-runner.js
CHANGED
|
@@ -126,7 +126,7 @@ async function runFlowBuild(agent, prompt, options) {
|
|
|
126
126
|
if (!options.signal?.aborted && typeof agent.emitWorkEvent === 'function') {
|
|
127
127
|
agent.emitWorkEvent({ type: 'error', content: reportedError.message, runId });
|
|
128
128
|
}
|
|
129
|
-
agent.finishConversationWorkRun(runId, options.signal?.aborted ? 'interrupted' : 'error');
|
|
129
|
+
agent.finishConversationWorkRun(runId, options.signal?.aborted ? 'interrupted' : 'error', undefined, options.signal?.aborted ? '' : reportedError.message);
|
|
130
130
|
agent.flushWorkspaceConversationState();
|
|
131
131
|
}
|
|
132
132
|
throw reportedError;
|
|
@@ -2,8 +2,11 @@ import { ModelValidationCache, ModelValidationRecord } from './modelValidation';
|
|
|
2
2
|
/** Durable validation evidence. The file contains probe metadata only, never prompts, tool arguments or credentials. */
|
|
3
3
|
export declare class FileModelValidationCache implements ModelValidationCache {
|
|
4
4
|
private readonly filePath;
|
|
5
|
+
private readonly readOnly;
|
|
5
6
|
private readonly records;
|
|
6
|
-
constructor(rootPath: string
|
|
7
|
+
constructor(rootPath: string, options?: {
|
|
8
|
+
readOnly?: boolean;
|
|
9
|
+
});
|
|
7
10
|
get(modelKey: string): ModelValidationRecord | undefined;
|
|
8
11
|
set(record: ModelValidationRecord): void;
|
|
9
12
|
delete(modelKey: string): void;
|
|
@@ -42,9 +42,11 @@ function key(model) {
|
|
|
42
42
|
/** Durable validation evidence. The file contains probe metadata only, never prompts, tool arguments or credentials. */
|
|
43
43
|
class FileModelValidationCache {
|
|
44
44
|
filePath;
|
|
45
|
+
readOnly;
|
|
45
46
|
records = new Map();
|
|
46
|
-
constructor(rootPath) {
|
|
47
|
+
constructor(rootPath, options = {}) {
|
|
47
48
|
this.filePath = path.join(rootPath, 'model-validation', 'records.json');
|
|
49
|
+
this.readOnly = options.readOnly === true;
|
|
48
50
|
this.load();
|
|
49
51
|
}
|
|
50
52
|
get(modelKey) {
|
|
@@ -53,11 +55,15 @@ class FileModelValidationCache {
|
|
|
53
55
|
}
|
|
54
56
|
set(record) {
|
|
55
57
|
this.records.set(record.modelKey || key(record.model), JSON.parse(JSON.stringify(record)));
|
|
58
|
+
if (this.readOnly)
|
|
59
|
+
return;
|
|
56
60
|
this.save();
|
|
57
61
|
}
|
|
58
62
|
delete(modelKey) {
|
|
59
63
|
if (!this.records.delete(modelKey))
|
|
60
64
|
return;
|
|
65
|
+
if (this.readOnly)
|
|
66
|
+
return;
|
|
61
67
|
this.save();
|
|
62
68
|
}
|
|
63
69
|
load() {
|
package/dist/core/workspace.d.ts
CHANGED
|
@@ -52,6 +52,12 @@ export declare class WorkspaceManager {
|
|
|
52
52
|
private saveState;
|
|
53
53
|
private findWorkspace;
|
|
54
54
|
private restoreCurrent;
|
|
55
|
+
/**
|
|
56
|
+
* Re-read the registry and persisted current-workspace pointer after another
|
|
57
|
+
* Newmark entrypoint updates Work/*.json. This intentionally does not create
|
|
58
|
+
* a workspace: a refresh must reflect the shared on-disk state exactly.
|
|
59
|
+
*/
|
|
60
|
+
reloadFromStorage(): WorkspaceInfo | null;
|
|
55
61
|
private saveInternal;
|
|
56
62
|
private saveExternal;
|
|
57
63
|
private sleepSync;
|
package/dist/core/workspace.js
CHANGED
|
@@ -358,6 +358,20 @@ class WorkspaceManager {
|
|
|
358
358
|
this.saveState();
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
|
+
/**
|
|
362
|
+
* Re-read the registry and persisted current-workspace pointer after another
|
|
363
|
+
* Newmark entrypoint updates Work/*.json. This intentionally does not create
|
|
364
|
+
* a workspace: a refresh must reflect the shared on-disk state exactly.
|
|
365
|
+
*/
|
|
366
|
+
reloadFromStorage() {
|
|
367
|
+
if (this.detached)
|
|
368
|
+
return this.current;
|
|
369
|
+
this.scan();
|
|
370
|
+
this.validate();
|
|
371
|
+
this.current = null;
|
|
372
|
+
this.restoreCurrent();
|
|
373
|
+
return this.current;
|
|
374
|
+
}
|
|
361
375
|
saveInternal() {
|
|
362
376
|
if (this.detached)
|
|
363
377
|
return;
|
|
@@ -56,6 +56,7 @@ export declare class WslAgentRuntimePool {
|
|
|
56
56
|
private hostToolHandler;
|
|
57
57
|
private restarting;
|
|
58
58
|
private disposing;
|
|
59
|
+
private forceStopPromises;
|
|
59
60
|
private capacityTail;
|
|
60
61
|
private accessSequence;
|
|
61
62
|
constructor(distro: string, windowsRoot: string, windowsHostScript: string, createClient?: WslTargetRuntimeClientFactory, options?: WslAgentRuntimePoolOptions);
|
|
@@ -90,6 +91,9 @@ export declare class WslAgentRuntimePool {
|
|
|
90
91
|
hasActiveWorkspace(target: ConversationRuntimeTarget): Promise<boolean>;
|
|
91
92
|
stopWorkspace(target: ConversationRuntimeTarget): Promise<void>;
|
|
92
93
|
stopTarget(target: ConversationRuntimeTarget): Promise<void>;
|
|
94
|
+
/** Hard-stop a target for destructive lifecycle actions such as archive. */
|
|
95
|
+
forceStopTarget(target: ConversationRuntimeTarget): Promise<void>;
|
|
96
|
+
private forceStopTargetInternal;
|
|
93
97
|
stopAll(): Promise<void>;
|
|
94
98
|
private throwStopFailures;
|
|
95
99
|
private stopEntry;
|
|
@@ -20,6 +20,7 @@ class WslAgentRuntimePool {
|
|
|
20
20
|
hostToolHandler = null;
|
|
21
21
|
restarting = new Set();
|
|
22
22
|
disposing = new Set();
|
|
23
|
+
forceStopPromises = new Map();
|
|
23
24
|
capacityTail = Promise.resolve();
|
|
24
25
|
accessSequence = 0;
|
|
25
26
|
constructor(distro, windowsRoot, windowsHostScript, createClient = (target) => new wslAgentClient_1.WslAgentClient(distro, windowsRoot, windowsHostScript, target), options = {}) {
|
|
@@ -356,6 +357,61 @@ class WslAgentRuntimePool {
|
|
|
356
357
|
if (entry)
|
|
357
358
|
await this.stopEntry(entry);
|
|
358
359
|
}
|
|
360
|
+
/** Hard-stop a target for destructive lifecycle actions such as archive. */
|
|
361
|
+
async forceStopTarget(target) {
|
|
362
|
+
const normalized = (0, conversationTarget_1.normalizeConversationTarget)(target);
|
|
363
|
+
const existing = this.forceStopPromises.get(normalized.runtimeKey);
|
|
364
|
+
if (existing)
|
|
365
|
+
return existing;
|
|
366
|
+
const operation = this.forceStopTargetInternal(normalized);
|
|
367
|
+
this.forceStopPromises.set(normalized.runtimeKey, operation);
|
|
368
|
+
try {
|
|
369
|
+
await operation;
|
|
370
|
+
}
|
|
371
|
+
finally {
|
|
372
|
+
if (this.forceStopPromises.get(normalized.runtimeKey) === operation) {
|
|
373
|
+
this.forceStopPromises.delete(normalized.runtimeKey);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
async forceStopTargetInternal(target) {
|
|
378
|
+
let entry;
|
|
379
|
+
await this.serializeCapacity(async () => {
|
|
380
|
+
entry = this.entries.get(target.runtimeKey);
|
|
381
|
+
if (entry)
|
|
382
|
+
this.disposing.add(target.runtimeKey);
|
|
383
|
+
});
|
|
384
|
+
if (!entry)
|
|
385
|
+
return;
|
|
386
|
+
const intent = entry.stopIntent || {
|
|
387
|
+
runId: entry.lastRunId,
|
|
388
|
+
generation: entry.lastGeneration,
|
|
389
|
+
checkpointed: false,
|
|
390
|
+
forcePromise: null,
|
|
391
|
+
};
|
|
392
|
+
if (!entry.stopIntent)
|
|
393
|
+
entry.stopIntent = intent;
|
|
394
|
+
try {
|
|
395
|
+
await this.forceTerminateEntry(entry, intent);
|
|
396
|
+
if (entry.client.status().connected) {
|
|
397
|
+
if (entry.client.forceStopRuntimeGroup)
|
|
398
|
+
await entry.client.forceStopRuntimeGroup();
|
|
399
|
+
else
|
|
400
|
+
await entry.client.forceRestartRuntimeGroup();
|
|
401
|
+
}
|
|
402
|
+
if (entry.client.status().connected) {
|
|
403
|
+
throw new Error(`WSL runtime ${target.runtimeKey} remained connected after force stop`);
|
|
404
|
+
}
|
|
405
|
+
if (this.entries.get(target.runtimeKey) === entry) {
|
|
406
|
+
this.entries.delete(target.runtimeKey);
|
|
407
|
+
entry.unsubscribe();
|
|
408
|
+
entry.client.setHostToolHandler(null);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
finally {
|
|
412
|
+
this.disposing.delete(target.runtimeKey);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
359
415
|
async stopAll() {
|
|
360
416
|
const targets = Array.from(this.entries.values(), entry => entry.target);
|
|
361
417
|
const results = await Promise.allSettled(targets.map(target => this.stopTarget(target)));
|
package/dist/launcher.js
CHANGED
|
@@ -43,19 +43,27 @@ const agent_1 = require("./core/agent");
|
|
|
43
43
|
const flow_1 = require("./core/flow");
|
|
44
44
|
const flow_runner_1 = require("./core/flow-runner");
|
|
45
45
|
const cli_commands_1 = require("./cli-commands");
|
|
46
|
+
const cli_discovery_1 = require("./cli-discovery");
|
|
46
47
|
const installUpdate_1 = require("./core/installUpdate");
|
|
47
48
|
const cli_help_1 = require("./cli-help");
|
|
48
|
-
const
|
|
49
|
+
const rawArgs = process.argv.slice(2);
|
|
50
|
+
const args = rawArgs[0] === '--' ? rawArgs.slice(1) : rawArgs;
|
|
49
51
|
const hasCliCommand = args.some(a => cli_commands_1.CLI_COMMANDS.includes(a));
|
|
52
|
+
const isEdit = args[0] === 'edit';
|
|
53
|
+
const editFile = isEdit ? args[1] : '';
|
|
54
|
+
const isFlow = args[0] === 'flow';
|
|
50
55
|
const isHelpArg = !hasCliCommand && (args.some(arg => ['--help', '-h'].includes(arg.toLowerCase())) || args[0]?.toLowerCase() === 'help');
|
|
51
|
-
const isVersionArg = !hasCliCommand &&
|
|
56
|
+
const isVersionArg = !hasCliCommand && (0, cli_discovery_1.isVersionArgument)(args);
|
|
57
|
+
const isReadOnlyValidation = hasCliCommand && args.includes('validate-models') && !args.includes('--persist');
|
|
52
58
|
const isTui = args.some(arg => arg.toLowerCase() === '--tui');
|
|
53
59
|
const isGui = args.some(arg => arg.toLowerCase() === '--gui');
|
|
54
60
|
const isCli = args.includes('--cli');
|
|
55
61
|
const isServer = args.includes('--server');
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
62
|
+
const invalidArgument = (0, cli_discovery_1.invalidTopLevelArgument)(args);
|
|
63
|
+
if (invalidArgument) {
|
|
64
|
+
console.error(`Invalid Newmark argument: ${invalidArgument}`);
|
|
65
|
+
process.exit(2);
|
|
66
|
+
}
|
|
59
67
|
function pathArgValue(values, key) {
|
|
60
68
|
const prefix = `${key}=`;
|
|
61
69
|
const inlineIdx = values.findIndex(a => a.startsWith(prefix));
|
|
@@ -89,6 +97,15 @@ function pathArgValue(values, key) {
|
|
|
89
97
|
}
|
|
90
98
|
return best || parts.join(' ') || undefined;
|
|
91
99
|
}
|
|
100
|
+
function resolveTuiWorkspacePath(values, root) {
|
|
101
|
+
const explicitWorkspace = pathArgValue(values, '--workspace');
|
|
102
|
+
if (explicitWorkspace)
|
|
103
|
+
return explicitWorkspace;
|
|
104
|
+
// An explicitly isolated runtime must not silently register the caller's
|
|
105
|
+
// cwd as an external workspace. Keep the opt-in --workspace escape hatch,
|
|
106
|
+
// while making the safe one-argument form fully self-contained.
|
|
107
|
+
return pathArgValue(values, '--root') ? root : process.cwd();
|
|
108
|
+
}
|
|
92
109
|
function userRuntimeRoot() {
|
|
93
110
|
return path.join(os.homedir(), '.Newmark');
|
|
94
111
|
}
|
|
@@ -174,18 +191,30 @@ function writableRuntimeRoot(candidate) {
|
|
|
174
191
|
const explicitRoot = pathArgValue(args, '--root');
|
|
175
192
|
const root = explicitRoot ? writableRuntimeRoot(explicitRoot) : userRuntimeRoot();
|
|
176
193
|
if (isHelpArg) {
|
|
177
|
-
console.log((0, cli_help_1.newmarkHelpText)((0, installUpdate_1.currentAppVersion)()));
|
|
194
|
+
console.log(isFlow ? (0, cli_help_1.newmarkFlowHelpText)() : isEdit ? (0, cli_help_1.newmarkEditHelpText)() : (0, cli_help_1.newmarkHelpText)((0, installUpdate_1.currentAppVersion)()));
|
|
178
195
|
process.exit(0);
|
|
179
196
|
}
|
|
180
197
|
if (isVersionArg) {
|
|
181
198
|
console.log((0, installUpdate_1.currentAppVersion)());
|
|
182
199
|
process.exit(0);
|
|
183
200
|
}
|
|
184
|
-
|
|
201
|
+
const unknownCommand = !hasCliCommand && !isTui && !isGui && !isCli && !isServer && !isFlow && !isEdit
|
|
202
|
+
? (0, cli_discovery_1.unknownTopLevelCommand)(args)
|
|
203
|
+
: undefined;
|
|
204
|
+
if (unknownCommand) {
|
|
205
|
+
console.error(`Unknown Newmark command or argument: ${unknownCommand}. Run --help to see the supported entrypoints.`);
|
|
206
|
+
process.exit(2);
|
|
207
|
+
}
|
|
208
|
+
function firstRunInit(r, options = {}) {
|
|
185
209
|
fs.mkdirSync(r, { recursive: true });
|
|
186
|
-
|
|
210
|
+
// Legacy AppData migration is only valid for the canonical user runtime.
|
|
211
|
+
// An explicit --root is an isolation boundary (tests, portable roots, and
|
|
212
|
+
// caller-selected workspaces) and must never inherit the user's sessions,
|
|
213
|
+
// archives, providers, or workspaces.
|
|
214
|
+
if (path.resolve(r) === path.resolve(userRuntimeRoot()))
|
|
215
|
+
migrateLegacyRuntimeRoot(r);
|
|
187
216
|
const { ensureRootConfig } = require('./core/config');
|
|
188
|
-
ensureRootConfig(r);
|
|
217
|
+
ensureRootConfig(r, options);
|
|
189
218
|
if (!fs.existsSync(path.join(r, 'agent.md'))) {
|
|
190
219
|
fs.writeFileSync(path.join(r, 'agent.md'), '# Newmark Agent\n\nYou are a powerful coding assistant.\n', 'utf-8');
|
|
191
220
|
}
|
|
@@ -270,13 +299,13 @@ function launchGui() {
|
|
|
270
299
|
console.error('Unable to locate the Newmark GUI runtime. Reinstall newmark-agent with optional dependencies enabled, or install a Newmark desktop package.');
|
|
271
300
|
process.exit(1);
|
|
272
301
|
}
|
|
273
|
-
firstRunInit(root);
|
|
302
|
+
firstRunInit(root, { readOnly: isReadOnlyValidation });
|
|
274
303
|
if (isGui) {
|
|
275
304
|
launchGui();
|
|
276
305
|
}
|
|
277
306
|
else if (isTui) {
|
|
278
307
|
const { start } = require('./tui/src/app');
|
|
279
|
-
start({ root, workspacePath:
|
|
308
|
+
start({ root, workspacePath: resolveTuiWorkspacePath(args, root), desktopDist: __dirname });
|
|
280
309
|
}
|
|
281
310
|
else if (hasCliCommand) {
|
|
282
311
|
(0, cli_commands_1.runCliCommand)(root, args).then(handled => {
|
package/dist/llm/provider.d.ts
CHANGED
|
@@ -28,9 +28,12 @@ export declare class LLMProvider {
|
|
|
28
28
|
explicitProtocol?: ProviderProtocol | undefined;
|
|
29
29
|
openAIMode: OpenAITransportMode | boolean;
|
|
30
30
|
useProviderAdaptersV2: boolean;
|
|
31
|
+
requestTimeoutMs: number;
|
|
31
32
|
static nodeHttpTransport: ((method: 'GET' | 'POST', url: string, headers: Record<string, string>, body?: string) => Promise<NodeHttpResult>) | null;
|
|
32
33
|
static powershellTransport: ((method: 'GET' | 'POST', url: string, headers: Record<string, string>, body?: string) => Promise<NodeHttpResult>) | null;
|
|
33
|
-
constructor(name: string, baseUrl: string, apiKey: string, explicitProtocol?: ProviderProtocol | undefined, openAIMode?: OpenAITransportMode | boolean, useProviderAdaptersV2?: boolean);
|
|
34
|
+
constructor(name: string, baseUrl: string, apiKey: string, explicitProtocol?: ProviderProtocol | undefined, openAIMode?: OpenAITransportMode | boolean, useProviderAdaptersV2?: boolean, requestTimeoutMs?: number);
|
|
35
|
+
private effectiveRequestTimeout;
|
|
36
|
+
private withRequestTimeout;
|
|
34
37
|
intelligenceConfig(tier: string): IntelligenceConfig;
|
|
35
38
|
private reasoningEffort;
|
|
36
39
|
private applyChatReasoningEffort;
|
|
@@ -85,10 +88,10 @@ export declare class LLMProvider {
|
|
|
85
88
|
private toStreamUsage;
|
|
86
89
|
private shouldDowngradeToResponses;
|
|
87
90
|
/**
|
|
88
|
-
* Loopback-aware transport injected into adapter `execute`.
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
91
|
+
* Loopback-aware transport injected into adapter `execute`. Streaming
|
|
92
|
+
* requests retain the fetch-to-node fallback for transport failures, while
|
|
93
|
+
* a local deadline is returned directly so one request cannot become a
|
|
94
|
+
* second Windows fallback request.
|
|
92
95
|
*/
|
|
93
96
|
private buildProviderAdapterTransport;
|
|
94
97
|
private toTransportResponse;
|