newmark-agent 0.5.14 → 0.6.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.
- package/dist/conversation-utility-host.bundle.cjs +4565 -2998
- package/dist/conversation-utility-host.js +1 -1
- package/dist/core/agent.d.ts +108 -16
- package/dist/core/agent.js +698 -195
- package/dist/core/agentKernel/agent.d.ts +1 -0
- package/dist/core/agentKernel/agent.js +9 -0
- package/dist/core/agentKernelDiagnostics.d.ts +4 -6
- package/dist/core/agentKernelDiagnostics.js +12 -9
- package/dist/core/agentKernelRunner.d.ts +5 -0
- package/dist/core/agentKernelRunner.js +250 -107
- package/dist/core/autoRouter.js +17 -27
- package/dist/core/config.d.ts +2 -0
- package/dist/core/config.js +7 -1
- package/dist/core/conversationCommandState.d.ts +17 -0
- package/dist/core/conversationCommandState.js +140 -0
- package/dist/core/conversationKernel.d.ts +25 -4
- package/dist/core/conversationKernel.js +360 -89
- package/dist/core/conversationListEvent.d.ts +6 -0
- package/dist/core/conversationListEvent.js +14 -0
- package/dist/core/electronUtilityAgentClient.d.ts +4 -1
- package/dist/core/electronUtilityAgentClient.js +4 -4
- package/dist/core/electronUtilityRuntimePool.d.ts +8 -2
- package/dist/core/electronUtilityRuntimePool.js +11 -5
- package/dist/core/installUpdate.js +41 -29
- package/dist/core/modelResponseHealth.d.ts +17 -0
- package/dist/core/modelResponseHealth.js +193 -0
- package/dist/core/providerUsageAccounting.d.ts +47 -0
- package/dist/core/providerUsageAccounting.js +71 -0
- package/dist/core/requestContextEstimate.d.ts +18 -0
- package/dist/core/requestContextEstimate.js +54 -0
- package/dist/core/subagent.d.ts +89 -5
- package/dist/core/subagent.js +264 -41
- package/dist/core/subagentCommunication.d.ts +43 -0
- package/dist/core/subagentCommunication.js +167 -0
- package/dist/core/types.d.ts +34 -1
- package/dist/core/utilityAgentProtocol.d.ts +4 -0
- package/dist/core/workEventCoalescer.js +4 -1
- package/dist/core/wslAgentClient.d.ts +18 -5
- package/dist/core/wslAgentClient.js +79 -27
- package/dist/core/wslAgentProtocol.d.ts +7 -0
- package/dist/core/wslAgentRuntimePool.d.ts +8 -2
- package/dist/core/wslAgentRuntimePool.js +19 -6
- package/dist/core/wslRuntimeProcessTree.d.ts +10 -0
- package/dist/core/wslRuntimeProcessTree.js +104 -0
- package/dist/llm/provider.d.ts +5 -12
- package/dist/llm/provider.js +150 -269
- package/dist/main.js +582 -356
- package/dist/preload.js +3 -3
- package/dist/providers/chat-completions.adapter.d.ts +2 -0
- package/dist/providers/chat-completions.adapter.js +84 -36
- package/dist/providers/provider-adapter.d.ts +2 -0
- package/dist/providers/provider-events.d.ts +23 -14
- package/dist/providers/provider-events.js +148 -43
- package/dist/providers/provider-headers.d.ts +7 -3
- package/dist/providers/provider-headers.js +109 -39
- package/dist/providers/provider-request-compat.d.ts +6 -0
- package/dist/providers/provider-request-compat.js +37 -0
- package/dist/providers/responses.adapter.d.ts +2 -0
- package/dist/providers/responses.adapter.js +192 -126
- package/dist/server.d.ts +9 -2
- package/dist/server.js +100 -24
- package/dist/tools/index.js +17 -1
- package/dist/ui/index.html +2515 -693
- package/dist/ui/lucide-sprite.svg +7 -0
- package/dist/ui/startup.html +6 -6
- package/dist/wsl-agent-host.bundle.cjs +4575 -3006
- package/dist/wsl-agent-host.js +9 -4
- package/package.json +23 -4
|
@@ -66,15 +66,21 @@ class WslAgentRuntimePool {
|
|
|
66
66
|
this.release(entry, true);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
-
async snapshot(target) {
|
|
69
|
+
async snapshot(target, options = {}) {
|
|
70
70
|
const normalized = (0, conversationTarget_1.normalizeConversationTarget)(target);
|
|
71
71
|
const entry = await this.acquire(normalized);
|
|
72
72
|
let scheduleIdle = false;
|
|
73
73
|
try {
|
|
74
|
-
|
|
74
|
+
const customWindow = options.before != null || (options.window != null && options.window !== 200);
|
|
75
|
+
if (entry.stopIntent) {
|
|
76
|
+
if (customWindow)
|
|
77
|
+
throw new Error('Earlier history is temporarily unavailable while this conversation is stopping. Retry after it stops.');
|
|
75
78
|
return this.supervisorSnapshot(entry);
|
|
76
|
-
|
|
77
|
-
entry.
|
|
79
|
+
}
|
|
80
|
+
const result = await entry.client.snapshotTarget(normalized, options);
|
|
81
|
+
// A fetched older page must not replace the latest supervisor snapshot.
|
|
82
|
+
if (!customWindow)
|
|
83
|
+
entry.lastSnapshot = result;
|
|
78
84
|
const runtime = result.runtime;
|
|
79
85
|
const runtimeIdentity = result.runtime;
|
|
80
86
|
if (runtimeIdentity?.runId)
|
|
@@ -196,7 +202,7 @@ class WslAgentRuntimePool {
|
|
|
196
202
|
}
|
|
197
203
|
}
|
|
198
204
|
async queueAction(target, action, input = {}) {
|
|
199
|
-
const entry = await this.
|
|
205
|
+
const entry = await this.acquire((0, conversationTarget_1.normalizeConversationTarget)(target));
|
|
200
206
|
if (!entry || !entry.client.queueAction)
|
|
201
207
|
throw new Error('Target conversation is not running');
|
|
202
208
|
try {
|
|
@@ -623,7 +629,14 @@ class WslAgentRuntimePool {
|
|
|
623
629
|
scheduleIdle(entry) {
|
|
624
630
|
this.touch(entry);
|
|
625
631
|
const ttl = Math.max(10, Number(this.options.idleTtlMs ?? 5 * 60 * 1000));
|
|
626
|
-
const timer = setTimeout(() => {
|
|
632
|
+
const timer = setTimeout(() => {
|
|
633
|
+
void this.evictIfIdle(entry.target.runtimeKey, entry.lastUsedAt).catch(() => {
|
|
634
|
+
// Failed cleanup retains the entry and its client's explicit error /
|
|
635
|
+
// quarantine. A background timer must not turn that target-local
|
|
636
|
+
// uncertainty into an unhandled rejection in the shared supervisor.
|
|
637
|
+
// Explicit stopTarget/forceStopTarget callers still receive the error.
|
|
638
|
+
});
|
|
639
|
+
}, ttl);
|
|
627
640
|
timer.unref?.();
|
|
628
641
|
entry.idleTimer = timer;
|
|
629
642
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Linux termination uses pidfds, never a bare PID/PGID signal. */
|
|
2
|
+
export interface WslRuntimeProcessIdentity {
|
|
3
|
+
pid: number;
|
|
4
|
+
pgid: number;
|
|
5
|
+
sessionId: number;
|
|
6
|
+
startTimeTicks: string;
|
|
7
|
+
bootId: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function wslRuntimeProcessTreeCommand(identity: WslRuntimeProcessIdentity): string;
|
|
10
|
+
//# sourceMappingURL=wslRuntimeProcessTree.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wslRuntimeProcessTreeCommand = wslRuntimeProcessTreeCommand;
|
|
4
|
+
function wslRuntimeProcessTreeCommand(identity) {
|
|
5
|
+
const script = `import os,sys,json,signal,time,select
|
|
6
|
+
expected=json.loads(${JSON.stringify(JSON.stringify(identity))})
|
|
7
|
+
owned={}
|
|
8
|
+
complete=False
|
|
9
|
+
capture_deadline=time.monotonic()+2.0
|
|
10
|
+
def interrupted(signum,frame): raise RuntimeError('termination helper interrupted before ownership cleanup completed')
|
|
11
|
+
for signum in (signal.SIGTERM,signal.SIGHUP,signal.SIGINT,signal.SIGALRM): signal.signal(signum,interrupted)
|
|
12
|
+
signal.setitimer(signal.ITIMER_REAL,3.5)
|
|
13
|
+
def fail(message): raise RuntimeError(message)
|
|
14
|
+
def stat(pid):
|
|
15
|
+
try:
|
|
16
|
+
raw=open('/proc/'+str(pid)+'/stat').read()
|
|
17
|
+
except FileNotFoundError: return None
|
|
18
|
+
fields=raw[raw.rfind(')')+2:].split()
|
|
19
|
+
return {'pid':pid,'parentPid':int(fields[1]),'pgid':int(fields[2]),'sessionId':int(fields[3]),'startTimeTicks':fields[19],'state':fields[0]}
|
|
20
|
+
def same(a,b): return a is not None and a['pid']==b['pid'] and a['startTimeTicks']==b['startTimeTicks']
|
|
21
|
+
def exited(fd): return bool(select.select([fd],[],[],0)[0])
|
|
22
|
+
def hold(entry,depth):
|
|
23
|
+
before=stat(entry['pid'])
|
|
24
|
+
if not same(before,entry): fail('process identity changed before pidfd capture')
|
|
25
|
+
fd=os.pidfd_open(entry['pid'],0)
|
|
26
|
+
after=stat(entry['pid'])
|
|
27
|
+
if not same(after,entry):
|
|
28
|
+
os.close(fd)
|
|
29
|
+
fail('process identity changed while opening pidfd')
|
|
30
|
+
owned[entry['pid']]={'entry':entry,'fd':fd,'depth':depth}
|
|
31
|
+
if exited(fd): fail('owned process exited before descendant capture; tree is unconfirmed')
|
|
32
|
+
signal.pidfd_send_signal(fd,signal.SIGSTOP,None,0)
|
|
33
|
+
deadline=min(capture_deadline,time.monotonic()+0.4)
|
|
34
|
+
while time.monotonic()<deadline:
|
|
35
|
+
current=stat(entry['pid'])
|
|
36
|
+
if exited(fd) or not same(current,entry): fail('owned process exited during descendant capture; tree is unconfirmed')
|
|
37
|
+
if current['state'] in ('T','t'): return
|
|
38
|
+
time.sleep(0.005)
|
|
39
|
+
fail('owned process could not be paused; descendant tree is unconfirmed')
|
|
40
|
+
try:
|
|
41
|
+
if not hasattr(os,'pidfd_open') or not hasattr(signal,'pidfd_send_signal'):
|
|
42
|
+
fail('Python 3 with Linux pidfd_open/pidfd_send_signal is required; refusing numeric PID fallback')
|
|
43
|
+
boot=open('/proc/sys/kernel/random/boot_id').read().strip().lower()
|
|
44
|
+
if boot!=expected['bootId']: fail('runtime boot identity mismatch; refusing stale tree signal')
|
|
45
|
+
leader=stat(expected['pid'])
|
|
46
|
+
if leader is None: fail('original runtime leader is absent; descendant ownership is unconfirmed')
|
|
47
|
+
if not same(leader,expected) or leader['pgid']!=expected['pgid'] or leader['sessionId']!=expected['sessionId']:
|
|
48
|
+
fail('runtime identity mismatch; refusing stale tree signal')
|
|
49
|
+
hold(leader,0)
|
|
50
|
+
while True:
|
|
51
|
+
if time.monotonic()>capture_deadline: fail('descendant capture deadline exceeded')
|
|
52
|
+
current={}
|
|
53
|
+
for name in os.listdir('/proc'):
|
|
54
|
+
if time.monotonic()>capture_deadline: fail('descendant scan deadline exceeded')
|
|
55
|
+
if name.isdigit():
|
|
56
|
+
row=stat(int(name))
|
|
57
|
+
if row is not None: current[row['pid']]=row
|
|
58
|
+
added=False
|
|
59
|
+
for pid,item in list(owned.items()):
|
|
60
|
+
if exited(item['fd']) or not same(current.get(pid),item['entry']):
|
|
61
|
+
fail('known ancestor exited before tree capture completed')
|
|
62
|
+
for row in current.values():
|
|
63
|
+
if time.monotonic()>capture_deadline: fail('descendant scan deadline exceeded')
|
|
64
|
+
if row['pid'] in owned: continue
|
|
65
|
+
parent=owned.get(row['parentPid'])
|
|
66
|
+
if parent is None: continue
|
|
67
|
+
if not same(current.get(row['parentPid']),parent['entry']): fail('parent identity changed')
|
|
68
|
+
if int(row['startTimeTicks'])<int(parent['entry']['startTimeTicks']): fail('child predates claimed parent')
|
|
69
|
+
if len(owned)>=1024: fail('owned descendant limit exceeded')
|
|
70
|
+
hold(row,parent['depth']+1)
|
|
71
|
+
added=True
|
|
72
|
+
if not added: break
|
|
73
|
+
# Every proven ancestor is paused, so no new child can escape the final scan.
|
|
74
|
+
# pidfd_send_signal keeps the kernel process identity even if its number is
|
|
75
|
+
# recycled after these checks; no process.kill/killpg fallback is permitted.
|
|
76
|
+
for item in sorted(owned.values(),key=lambda x:x['depth'],reverse=True):
|
|
77
|
+
try: signal.pidfd_send_signal(item['fd'],signal.SIGKILL,None,0)
|
|
78
|
+
except ProcessLookupError: pass
|
|
79
|
+
deadline=time.monotonic()+1.2
|
|
80
|
+
while time.monotonic()<deadline and any(not exited(item['fd']) for item in owned.values()): time.sleep(0.01)
|
|
81
|
+
if any(not exited(item['fd']) for item in owned.values()): fail('owned pidfd termination remains unconfirmed')
|
|
82
|
+
complete=True
|
|
83
|
+
print(json.dumps({'terminated':True,'method':'pidfd_tree','rootPid':expected['pid'],'entries':[item['entry'] for item in owned.values()]}))
|
|
84
|
+
except Exception as error:
|
|
85
|
+
signal.setitimer(signal.ITIMER_REAL,0)
|
|
86
|
+
for signum in (signal.SIGTERM,signal.SIGHUP,signal.SIGINT,signal.SIGALRM): signal.signal(signum,signal.SIG_IGN)
|
|
87
|
+
# On uncertainty only the already captured handles remain safe to signal.
|
|
88
|
+
for item in sorted(owned.values(),key=lambda x:x['depth'],reverse=True):
|
|
89
|
+
try: signal.pidfd_send_signal(item['fd'],signal.SIGKILL,None,0)
|
|
90
|
+
except ProcessLookupError: pass
|
|
91
|
+
except OSError:
|
|
92
|
+
# If termination itself is denied, do not strand a process we held.
|
|
93
|
+
try: signal.pidfd_send_signal(item['fd'],signal.SIGCONT,None,0)
|
|
94
|
+
except OSError: pass
|
|
95
|
+
print('WSL runtime tree termination unconfirmed: '+str(error),file=sys.stderr)
|
|
96
|
+
sys.exit(73)
|
|
97
|
+
finally:
|
|
98
|
+
signal.setitimer(signal.ITIMER_REAL,0)
|
|
99
|
+
for item in owned.values(): os.close(item['fd'])
|
|
100
|
+
`;
|
|
101
|
+
const encoded = Buffer.from(script, 'utf8').toString('base64');
|
|
102
|
+
return `printf %s '${encoded}' | base64 -d | python3 -`;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=wslRuntimeProcessTree.js.map
|
package/dist/llm/provider.d.ts
CHANGED
|
@@ -127,25 +127,18 @@ export declare class LLMProvider {
|
|
|
127
127
|
* transport/provider failure may end the request.
|
|
128
128
|
*/
|
|
129
129
|
private buildProviderAdapterTransport;
|
|
130
|
+
private bufferTransportResponse;
|
|
130
131
|
private toTransportResponse;
|
|
131
132
|
private toNormalizedMessages;
|
|
132
133
|
private toNormalizedTools;
|
|
133
134
|
chatStreamWithTools(model: string, messages: Array<Record<string, unknown>>, systemPrompt: string | null, temperature: number, maxTokens: number, tools: unknown[], signal?: AbortSignal, reasoningTier?: string, sessionId?: string): AsyncGenerator<StreamToken>;
|
|
134
135
|
/**
|
|
135
|
-
* GitHub Models
|
|
136
|
-
*
|
|
137
|
-
* inference URL or its X-GitHub-Api-Version headers. OpenAI protocol
|
|
138
|
-
* providers route exclusively through chatStreamWithToolsV2.
|
|
136
|
+
* GitHub Models keeps its dedicated URL, headers and request serialization.
|
|
137
|
+
* Response parsing shares the Chat adapter's completion validation.
|
|
139
138
|
*/
|
|
140
139
|
private githubModelsChatStreamWithTools;
|
|
141
|
-
/**
|
|
142
|
-
* GitHub Models non-streaming fallback used when the streaming fetch fails
|
|
143
|
-
* and the node-http transport is available. Mirrors the legacy chat-tools
|
|
144
|
-
* node fallback; the responses downgrade is intentionally not applied for
|
|
145
|
-
* GitHub Models (its inference endpoint is chat-completions only).
|
|
146
|
-
*/
|
|
147
|
-
private githubModelsChatNonStreaming;
|
|
148
140
|
private anthropicChatWithTools;
|
|
141
|
+
private nativeJsonCompletionError;
|
|
149
142
|
/**
|
|
150
143
|
* Deterministic capability probe that sends the schema through the native
|
|
151
144
|
* protocol field. Prompt-only JSON requests must not be treated as evidence
|
|
@@ -157,7 +150,7 @@ export declare class LLMProvider {
|
|
|
157
150
|
* the socket closes is insufficient: truncated SSE must not validate.
|
|
158
151
|
*/
|
|
159
152
|
probeStreamCompletion(model: string, messages: Array<Record<string, unknown>>, systemPrompt: string | null, temperature: number, maxTokens: number, signal?: AbortSignal): Promise<ProviderStreamProbeResult>;
|
|
160
|
-
chat(model: string, messages: Array<Record<string, unknown>>, systemPrompt: string | null, temperature: number, maxTokens: number, signal?: AbortSignal, reasoningTier?: string): Promise<string>;
|
|
153
|
+
chat(model: string, messages: Array<Record<string, unknown>>, systemPrompt: string | null, temperature: number, maxTokens: number, signal?: AbortSignal, reasoningTier?: string, onUsage?: (usage: NonNullable<StreamToken['usage']>) => void): Promise<string>;
|
|
161
154
|
modelCatalog(): Promise<ProviderModelCatalogEntry[]>;
|
|
162
155
|
listModels(): Promise<string[]>;
|
|
163
156
|
validate(model: string): Promise<{
|