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.
Files changed (68) hide show
  1. package/dist/conversation-utility-host.bundle.cjs +4565 -2998
  2. package/dist/conversation-utility-host.js +1 -1
  3. package/dist/core/agent.d.ts +108 -16
  4. package/dist/core/agent.js +698 -195
  5. package/dist/core/agentKernel/agent.d.ts +1 -0
  6. package/dist/core/agentKernel/agent.js +9 -0
  7. package/dist/core/agentKernelDiagnostics.d.ts +4 -6
  8. package/dist/core/agentKernelDiagnostics.js +12 -9
  9. package/dist/core/agentKernelRunner.d.ts +5 -0
  10. package/dist/core/agentKernelRunner.js +250 -107
  11. package/dist/core/autoRouter.js +17 -27
  12. package/dist/core/config.d.ts +2 -0
  13. package/dist/core/config.js +7 -1
  14. package/dist/core/conversationCommandState.d.ts +17 -0
  15. package/dist/core/conversationCommandState.js +140 -0
  16. package/dist/core/conversationKernel.d.ts +25 -4
  17. package/dist/core/conversationKernel.js +360 -89
  18. package/dist/core/conversationListEvent.d.ts +6 -0
  19. package/dist/core/conversationListEvent.js +14 -0
  20. package/dist/core/electronUtilityAgentClient.d.ts +4 -1
  21. package/dist/core/electronUtilityAgentClient.js +4 -4
  22. package/dist/core/electronUtilityRuntimePool.d.ts +8 -2
  23. package/dist/core/electronUtilityRuntimePool.js +11 -5
  24. package/dist/core/installUpdate.js +41 -29
  25. package/dist/core/modelResponseHealth.d.ts +17 -0
  26. package/dist/core/modelResponseHealth.js +193 -0
  27. package/dist/core/providerUsageAccounting.d.ts +47 -0
  28. package/dist/core/providerUsageAccounting.js +71 -0
  29. package/dist/core/requestContextEstimate.d.ts +18 -0
  30. package/dist/core/requestContextEstimate.js +54 -0
  31. package/dist/core/subagent.d.ts +89 -5
  32. package/dist/core/subagent.js +264 -41
  33. package/dist/core/subagentCommunication.d.ts +43 -0
  34. package/dist/core/subagentCommunication.js +167 -0
  35. package/dist/core/types.d.ts +34 -1
  36. package/dist/core/utilityAgentProtocol.d.ts +4 -0
  37. package/dist/core/workEventCoalescer.js +4 -1
  38. package/dist/core/wslAgentClient.d.ts +18 -5
  39. package/dist/core/wslAgentClient.js +79 -27
  40. package/dist/core/wslAgentProtocol.d.ts +7 -0
  41. package/dist/core/wslAgentRuntimePool.d.ts +8 -2
  42. package/dist/core/wslAgentRuntimePool.js +19 -6
  43. package/dist/core/wslRuntimeProcessTree.d.ts +10 -0
  44. package/dist/core/wslRuntimeProcessTree.js +104 -0
  45. package/dist/llm/provider.d.ts +5 -12
  46. package/dist/llm/provider.js +150 -269
  47. package/dist/main.js +582 -356
  48. package/dist/preload.js +3 -3
  49. package/dist/providers/chat-completions.adapter.d.ts +2 -0
  50. package/dist/providers/chat-completions.adapter.js +84 -36
  51. package/dist/providers/provider-adapter.d.ts +2 -0
  52. package/dist/providers/provider-events.d.ts +23 -14
  53. package/dist/providers/provider-events.js +148 -43
  54. package/dist/providers/provider-headers.d.ts +7 -3
  55. package/dist/providers/provider-headers.js +109 -39
  56. package/dist/providers/provider-request-compat.d.ts +6 -0
  57. package/dist/providers/provider-request-compat.js +37 -0
  58. package/dist/providers/responses.adapter.d.ts +2 -0
  59. package/dist/providers/responses.adapter.js +192 -126
  60. package/dist/server.d.ts +9 -2
  61. package/dist/server.js +100 -24
  62. package/dist/tools/index.js +17 -1
  63. package/dist/ui/index.html +2515 -693
  64. package/dist/ui/lucide-sprite.svg +7 -0
  65. package/dist/ui/startup.html +6 -6
  66. package/dist/wsl-agent-host.bundle.cjs +4575 -3006
  67. package/dist/wsl-agent-host.js +9 -4
  68. 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
- if (entry.stopIntent)
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
- const result = await entry.client.snapshotTarget(normalized);
77
- entry.lastSnapshot = result;
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.acquireExisting(target);
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(() => { void this.evictIfIdle(entry.target.runtimeKey, entry.lastUsedAt); }, ttl);
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
@@ -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 streaming path. Preserved as a dedicated implementation
136
- * because the provider adapters (V2) do not serialize the GitHub Models
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<{