newmark-agent 0.5.14 → 0.5.15

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 (64) hide show
  1. package/dist/conversation-utility-host.bundle.cjs +3373 -1900
  2. package/dist/conversation-utility-host.js +1 -1
  3. package/dist/core/agent.d.ts +105 -14
  4. package/dist/core/agent.js +658 -104
  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 +236 -85
  11. package/dist/core/autoRouter.js +16 -3
  12. package/dist/core/conversationCommandState.d.ts +17 -0
  13. package/dist/core/conversationCommandState.js +140 -0
  14. package/dist/core/conversationKernel.d.ts +25 -4
  15. package/dist/core/conversationKernel.js +347 -83
  16. package/dist/core/conversationListEvent.d.ts +6 -0
  17. package/dist/core/conversationListEvent.js +14 -0
  18. package/dist/core/electronUtilityAgentClient.d.ts +4 -1
  19. package/dist/core/electronUtilityAgentClient.js +4 -4
  20. package/dist/core/electronUtilityRuntimePool.d.ts +8 -2
  21. package/dist/core/electronUtilityRuntimePool.js +11 -5
  22. package/dist/core/installUpdate.js +41 -29
  23. package/dist/core/providerUsageAccounting.d.ts +47 -0
  24. package/dist/core/providerUsageAccounting.js +71 -0
  25. package/dist/core/requestContextEstimate.d.ts +18 -0
  26. package/dist/core/requestContextEstimate.js +54 -0
  27. package/dist/core/subagent.d.ts +89 -5
  28. package/dist/core/subagent.js +264 -41
  29. package/dist/core/subagentCommunication.d.ts +43 -0
  30. package/dist/core/subagentCommunication.js +167 -0
  31. package/dist/core/types.d.ts +32 -1
  32. package/dist/core/utilityAgentProtocol.d.ts +4 -0
  33. package/dist/core/workEventCoalescer.js +4 -1
  34. package/dist/core/wslAgentClient.d.ts +18 -5
  35. package/dist/core/wslAgentClient.js +79 -27
  36. package/dist/core/wslAgentProtocol.d.ts +7 -0
  37. package/dist/core/wslAgentRuntimePool.d.ts +8 -2
  38. package/dist/core/wslAgentRuntimePool.js +19 -6
  39. package/dist/core/wslRuntimeProcessTree.d.ts +10 -0
  40. package/dist/core/wslRuntimeProcessTree.js +104 -0
  41. package/dist/llm/provider.d.ts +5 -12
  42. package/dist/llm/provider.js +148 -267
  43. package/dist/main.js +582 -356
  44. package/dist/preload.js +3 -3
  45. package/dist/providers/chat-completions.adapter.d.ts +2 -0
  46. package/dist/providers/chat-completions.adapter.js +84 -36
  47. package/dist/providers/provider-adapter.d.ts +2 -0
  48. package/dist/providers/provider-events.d.ts +23 -14
  49. package/dist/providers/provider-events.js +148 -43
  50. package/dist/providers/provider-headers.d.ts +7 -3
  51. package/dist/providers/provider-headers.js +109 -39
  52. package/dist/providers/provider-request-compat.d.ts +6 -0
  53. package/dist/providers/provider-request-compat.js +37 -0
  54. package/dist/providers/responses.adapter.d.ts +2 -0
  55. package/dist/providers/responses.adapter.js +192 -126
  56. package/dist/server.d.ts +9 -2
  57. package/dist/server.js +100 -24
  58. package/dist/tools/index.js +17 -1
  59. package/dist/ui/index.html +2374 -616
  60. package/dist/ui/lucide-sprite.svg +7 -0
  61. package/dist/ui/startup.html +6 -6
  62. package/dist/wsl-agent-host.bundle.cjs +3381 -1906
  63. package/dist/wsl-agent-host.js +9 -4
  64. package/package.json +23 -4
@@ -0,0 +1,6 @@
1
+ import type { Agent } from './agent';
2
+ import type { AgentWorkEvent } from './types';
3
+ import type { WorkspaceInfo } from './workspace';
4
+ /** Directory changes are workspace metadata, never a turn or selection command. */
5
+ export declare function conversationListEvent(agent: Pick<Agent, 'listWorkspaceConversationStates'>, workspace: WorkspaceInfo): AgentWorkEvent;
6
+ //# sourceMappingURL=conversationListEvent.d.ts.map
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.conversationListEvent = conversationListEvent;
4
+ const crypto_1 = require("crypto");
5
+ /** Directory changes are workspace metadata, never a turn or selection command. */
6
+ function conversationListEvent(agent, workspace) {
7
+ return {
8
+ id: (0, crypto_1.randomUUID)(), type: 'conversation_list', stateScope: 'workspace',
9
+ workspaceId: String(workspace.id || workspace.path), conversationId: '',
10
+ conversations: agent.listWorkspaceConversationStates(workspace),
11
+ content: '', mode: '', model: '', timestamp: new Date().toISOString(),
12
+ };
13
+ }
14
+ //# sourceMappingURL=conversationListEvent.js.map
@@ -92,7 +92,10 @@ export declare class ElectronUtilityAgentClient {
92
92
  start(): Promise<void>;
93
93
  private startNewChild;
94
94
  prompt(params: UtilityPromptRequest): Promise<UtilityAgentPromptResult>;
95
- snapshot(): Promise<UtilityAgentSnapshotResult>;
95
+ snapshot(options?: {
96
+ window?: number;
97
+ before?: number;
98
+ }): Promise<UtilityAgentSnapshotResult>;
96
99
  rewind(messageIndex: number): Promise<UtilityConversationRewindResult>;
97
100
  requestStop(runId?: string): Promise<UtilityAgentStopResult>;
98
101
  enqueueGuide(envelope: ConversationInputEnvelope): Promise<GuideReceipt>;
@@ -1010,9 +1010,9 @@ class ElectronUtilityAgentClient {
1010
1010
  await this.start();
1011
1011
  return await this.request('prompt', { ...params, target: this.checkedTarget(params.target) }, 0);
1012
1012
  }
1013
- async snapshot() {
1013
+ async snapshot(options = {}) {
1014
1014
  await this.start();
1015
- return await this.requestTargetSnapshot();
1015
+ return await this.requestTargetSnapshot(options);
1016
1016
  }
1017
1017
  async rewind(messageIndex) {
1018
1018
  await this.start();
@@ -1222,8 +1222,8 @@ class ElectronUtilityAgentClient {
1222
1222
  ]);
1223
1223
  return confirmed || this.child !== child;
1224
1224
  }
1225
- async requestTargetSnapshot() {
1226
- const snapshot = await this.request('snapshot', { target: this.target }, 15_000);
1225
+ async requestTargetSnapshot(options = {}) {
1226
+ const snapshot = await this.request('snapshot', { target: this.target, options }, 15_000);
1227
1227
  if (snapshot?.target?.runtimeKey !== this.target.runtimeKey) {
1228
1228
  throw new Error('Electron utility runtime snapshot target mismatch');
1229
1229
  }
@@ -7,7 +7,10 @@ export interface ElectronTargetRuntimeClient {
7
7
  subscribe(listener: (event: AgentWorkEvent) => void): () => void;
8
8
  setHostToolHandler(handler: UtilityHostToolHandler | null): void;
9
9
  prompt(params: UtilityPromptRequest): Promise<UtilityAgentPromptResult>;
10
- snapshot(): Promise<UtilityAgentSnapshotResult>;
10
+ snapshot(options?: {
11
+ window?: number;
12
+ before?: number;
13
+ }): Promise<UtilityAgentSnapshotResult>;
11
14
  rewind(messageIndex: number): Promise<UtilityConversationRewindResult>;
12
15
  requestStop(runId?: string): Promise<UtilityAgentStopResult>;
13
16
  enqueueGuide(envelope: ConversationInputEnvelope): Promise<GuideReceipt>;
@@ -69,7 +72,10 @@ export declare class ElectronUtilityRuntimePool {
69
72
  subscribe(listener: (event: AgentWorkEvent) => void): () => void;
70
73
  setHostToolHandler(handler: UtilityHostToolHandler | null): void;
71
74
  prompt(params: UtilityPromptRequest): Promise<UtilityAgentPromptResult>;
72
- snapshot(target: ConversationRuntimeTarget): Promise<UtilityAgentSnapshotResult>;
75
+ snapshot(target: ConversationRuntimeTarget, options?: {
76
+ window?: number;
77
+ before?: number;
78
+ }): Promise<UtilityAgentSnapshotResult>;
73
79
  rewind(target: ConversationRuntimeTarget, messageIndex: number): Promise<UtilityConversationRewindResult>;
74
80
  requestStop(target: ConversationRuntimeTarget, runId?: string): Promise<ElectronPoolStopResult>;
75
81
  enqueueGuide(envelope: ConversationInputEnvelope): Promise<GuideReceipt>;
@@ -50,14 +50,20 @@ class ElectronUtilityRuntimePool {
50
50
  this.release(entry, true);
51
51
  }
52
52
  }
53
- async snapshot(target) {
53
+ async snapshot(target, options = {}) {
54
54
  const entry = await this.acquire((0, conversationTarget_1.normalizeConversationTarget)(target));
55
55
  let scheduleIdle = false;
56
56
  try {
57
- if (entry.stopIntent)
57
+ const customWindow = options.before != null || (options.window != null && options.window !== 200);
58
+ if (entry.stopIntent) {
59
+ if (customWindow)
60
+ throw new Error('Earlier history is temporarily unavailable while this conversation is stopping. Retry after it stops.');
58
61
  return this.supervisorSnapshot(entry);
59
- const result = await entry.client.snapshot();
60
- entry.lastSnapshot = result;
62
+ }
63
+ const result = await entry.client.snapshot(options);
64
+ // A fetched older page must not replace the latest supervisor snapshot.
65
+ if (!customWindow)
66
+ entry.lastSnapshot = result;
61
67
  if (result.runtime?.runId)
62
68
  entry.lastRunId = result.runtime.runId;
63
69
  if (result.runtime?.generation)
@@ -185,7 +191,7 @@ class ElectronUtilityRuntimePool {
185
191
  }
186
192
  }
187
193
  async queueAction(target, action, input = {}) {
188
- const entry = await this.acquireExisting(target);
194
+ const entry = await this.acquire((0, conversationTarget_1.normalizeConversationTarget)(target));
189
195
  if (!entry || !entry.client.queueAction)
190
196
  throw new Error('Target conversation is not running');
191
197
  try {
@@ -601,11 +601,12 @@ function runMsiExec(args) {
601
601
  }
602
602
  function windowsCommandQuote(value) {
603
603
  const text = String(value);
604
- return /\s/.test(text) ? `"${text.replace(/"/g, '\\"')}"` : text;
604
+ return `"${text.replace(/(\\*)"/g, '$1$1\\"').replace(/(\\+)$/g, '$1$1')}"`;
605
605
  }
606
606
  function runElevatedMsiExec(args) {
607
607
  const argumentString = args.map(arg => windowsCommandQuote(arg)).join(' ');
608
608
  const script = [
609
+ '$ErrorActionPreference = "Stop"',
609
610
  `$argumentList = '${argumentString.replace(/'/g, "''")}'`,
610
611
  '$process = Start-Process -FilePath "msiexec.exe" -ArgumentList $argumentList -Verb RunAs -Wait -PassThru',
611
612
  'Write-Output ("EXITCODE:" + $process.ExitCode)',
@@ -614,7 +615,8 @@ function runElevatedMsiExec(args) {
614
615
  const stdout = String(result.stdout || '');
615
616
  const match = stdout.match(/EXITCODE:(\d+)/);
616
617
  return {
617
- exitCode: match ? Number(match[1]) : (result.status === null ? -1 : result.status),
618
+ // A shell exit of 0 without the MSI completion marker is not installation.
619
+ exitCode: match ? Number(match[1]) : -1,
618
620
  stdout,
619
621
  stderr: String(result.stderr || ''),
620
622
  };
@@ -633,17 +635,39 @@ function uninstallNewmarkProduct(productCode, logPath) {
633
635
  };
634
636
  }
635
637
  function installMsiPackage(msiPath, options = {}) {
636
- const logPath = path.join(options.logDir || os.tmpdir(), `newmark-msi-install-${process.pid}-${Date.now()}.log`);
637
- const args = ['/i', path.resolve(msiPath), '/qn', '/norestart', '/l*v', logPath];
638
- let result = runMsiExec(args);
639
- if (result.exitCode !== 0 && result.exitCode !== 3010 && options.allowElevate !== false)
640
- result = runElevatedMsiExec(args);
641
- const ok = result.exitCode === 0 || result.exitCode === 3010;
638
+ const logDir = path.resolve(options.logDir || os.tmpdir());
639
+ fs.mkdirSync(logDir, { recursive: true });
640
+ const evidence = fs.mkdtempSync(path.join(logDir, 'newmark-msi-install-'));
641
+ const logPath = path.join(evidence, 'result.json');
642
+ // The same verified workflow owns CLI, GUI, and developer installs. MSI major
643
+ // upgrades handle old ProductCodes; only an already-installed exact ProductCode
644
+ // uses REINSTALL=ALL + vamus. Fresh installs must select ADDLOCAL=ALL instead.
645
+ const helperCandidates = [
646
+ path.join(process.resourcesPath || path.dirname(process.execPath), 'installer', 'install-windows-msi.ps1'),
647
+ path.resolve(__dirname, '..', '..', 'scripts', 'install-windows-msi.ps1'),
648
+ ];
649
+ const helper = helperCandidates.find(candidate => fs.existsSync(candidate) && !candidate.includes(`${path.sep}app.asar${path.sep}`));
650
+ if (!helper)
651
+ return { ok: false, exitCode: -1, logPath, error: 'Verified MSI installer helper is missing from this application.' };
652
+ const args = ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', helper,
653
+ '-MsiPath', path.resolve(msiPath), '-EvidenceDirectory', evidence];
654
+ if (options.allowElevate === false)
655
+ args.push('-NoElevate');
656
+ // Node passes argv directly; no Start-Process ArgumentList array re-parsing.
657
+ // Do not detach or impose a timeout which abandons an active MSI transaction.
658
+ const result = (0, child_process_1.spawnSync)('powershell.exe', args, { encoding: 'utf-8', windowsHide: true });
659
+ let completion = {};
660
+ try {
661
+ completion = JSON.parse(fs.readFileSync(logPath, 'utf-8').replace(/^\uFEFF/, ''));
662
+ }
663
+ catch { /* Missing evidence is failure. */ }
664
+ const ok = result.status === 0 && completion.Success === true;
665
+ const exitCode = ok ? (completion.Steps?.some(step => step.ExitCode === 3010) ? 3010 : 0) : (result.status || -1);
642
666
  return {
643
667
  ok,
644
- exitCode: result.exitCode,
668
+ exitCode,
645
669
  logPath,
646
- error: ok ? undefined : `msiexec install exited ${result.exitCode}`,
670
+ error: ok ? undefined : (completion.Error || result.error?.message || String(result.stderr || '').trim() || 'MSI installation has no verified completion receipt.'),
647
671
  };
648
672
  }
649
673
  function findLegacyNewmarkExecutables(excludeRoots = []) {
@@ -752,29 +776,17 @@ function executeManagedMsiInstall(msiPath, options = {}) {
752
776
  ? stopNewmarkProcesses(plan.runningProcesses.map(process => process.pid))
753
777
  : { stopped: [], errors: [] };
754
778
  const uninstalled = [];
755
- if (options.uninstallPrevious !== false) {
756
- for (const product of plan.installedProducts) {
757
- const logPath = path.join(options.logDir || os.tmpdir(), `newmark-msi-uninstall-${product.productCode}-${process.pid}-${Date.now()}.log`);
758
- const uninstallResult = uninstallNewmarkProduct(product.productCode, logPath);
759
- if (!uninstallResult.ok) {
760
- return {
761
- ok: false,
762
- plan,
763
- stopped: stopResult.stopped,
764
- uninstalled,
765
- removedLegacy: [],
766
- exitCode: uninstallResult.exitCode,
767
- logPath: uninstallResult.logPath,
768
- error: `Failed to uninstall previous Newmark version ${product.productCode}: ${uninstallResult.error}`,
769
- };
770
- }
771
- uninstalled.push(product.productCode);
772
- }
773
- }
779
+ // AllowSameVersionUpgrades removes related old products within one MSI/UAC
780
+ // transaction. Pre-uninstalling here caused multiple prompts and a fresh
781
+ // product incorrectly sent through the old REINSTALL-only path.
774
782
  const removeResult = plan.legacyExecutables.length
775
783
  ? removeLegacyNewmarkExecutables(plan.legacyExecutables)
776
784
  : { removed: [], errors: [] };
777
785
  const installResult = installMsiPackage(plan.msiPath, { logDir: options.logDir, allowElevate: options.allowElevate });
786
+ if (installResult.ok) {
787
+ const remaining = new Set(listInstalledNewmarkProducts().map(product => product.productCode));
788
+ uninstalled.push(...plan.installedProducts.filter(product => !remaining.has(product.productCode)).map(product => product.productCode));
789
+ }
778
790
  return {
779
791
  ok: installResult.ok,
780
792
  plan,
@@ -0,0 +1,47 @@
1
+ /** Measured provider counters. Missing values are tracked separately from zero. */
2
+ export interface Counter {
3
+ input: number;
4
+ output: number;
5
+ cacheRead: number;
6
+ cacheWrite: number;
7
+ }
8
+ export interface Reported {
9
+ input: boolean;
10
+ output: boolean;
11
+ cacheRead: boolean;
12
+ cacheWrite: boolean;
13
+ }
14
+ export type UsageInput = Partial<Counter> & {
15
+ reported?: Partial<Reported>;
16
+ };
17
+ /** JSON-persistable conversation coverage; totals remain owned by the caller. */
18
+ export interface Accounting {
19
+ requests: number;
20
+ inputReportedRequests: number;
21
+ outputReportedRequests: number;
22
+ /** Requests with both input/cache read reported and cache read <= input. */
23
+ cacheReportedRequests: number;
24
+ cacheEligibleInputTokens: number;
25
+ cacheReadTokens: number;
26
+ hasLegacyTotals: boolean;
27
+ lastInputTokens: number | null;
28
+ }
29
+ /** Retain this handle for all cumulative usage events from one provider request. */
30
+ export interface AccountingRequest {
31
+ usage: Counter;
32
+ reported: Reported;
33
+ }
34
+ export declare function createAccounting(legacyTotals?: Partial<Counter> | null): Accounting;
35
+ export declare function beginAccountingRequest(accounting: Accounting): AccountingRequest;
36
+ /**
37
+ * Provider stream usage is cumulative per request, including partial updates.
38
+ * Replacing each reported field contributes only its delta, including negative
39
+ * corrections. Unreported/invalid fields never erase a previous measurement.
40
+ */
41
+ export declare function applyAccountingUsage(accounting: Accounting, totals: Counter, request: AccountingRequest, input: UsageInput): void;
42
+ export declare function ratioSummary(accounting: Accounting): {
43
+ totalRatio: number | null;
44
+ knownRatio: number | null;
45
+ denominator: number;
46
+ };
47
+ //# sourceMappingURL=providerUsageAccounting.d.ts.map
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAccounting = createAccounting;
4
+ exports.beginAccountingRequest = beginAccountingRequest;
5
+ exports.applyAccountingUsage = applyAccountingUsage;
6
+ exports.ratioSummary = ratioSummary;
7
+ const fields = ['input', 'output', 'cacheRead', 'cacheWrite'];
8
+ function createAccounting(legacyTotals) {
9
+ return {
10
+ requests: 0,
11
+ inputReportedRequests: 0,
12
+ outputReportedRequests: 0,
13
+ cacheReportedRequests: 0,
14
+ cacheEligibleInputTokens: 0,
15
+ cacheReadTokens: 0,
16
+ // Old persisted zeros do not establish whether any cache fields were reported.
17
+ hasLegacyTotals: legacyTotals != null,
18
+ lastInputTokens: null,
19
+ };
20
+ }
21
+ function beginAccountingRequest(accounting) {
22
+ accounting.requests++;
23
+ return {
24
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
25
+ reported: { input: false, output: false, cacheRead: false, cacheWrite: false },
26
+ };
27
+ }
28
+ function validCachePair(request) {
29
+ return request.reported.input && request.reported.cacheRead
30
+ && request.usage.cacheRead <= request.usage.input;
31
+ }
32
+ /**
33
+ * Provider stream usage is cumulative per request, including partial updates.
34
+ * Replacing each reported field contributes only its delta, including negative
35
+ * corrections. Unreported/invalid fields never erase a previous measurement.
36
+ */
37
+ function applyAccountingUsage(accounting, totals, request, input) {
38
+ const hadInput = request.reported.input;
39
+ const hadOutput = request.reported.output;
40
+ const hadPair = validCachePair(request);
41
+ const previousEligibleInput = hadPair ? request.usage.input : 0;
42
+ const previousEligibleCache = hadPair ? request.usage.cacheRead : 0;
43
+ for (const field of fields) {
44
+ const value = input[field];
45
+ // Older internal callers supply actual numeric fields without metadata.
46
+ // Once metadata exists, only its explicit true flags represent reporting.
47
+ const reported = input.reported === undefined || input.reported[field] === true;
48
+ if (!reported || typeof value !== 'number' || !Number.isFinite(value) || value < 0)
49
+ continue;
50
+ totals[field] += value - request.usage[field];
51
+ request.usage[field] = value;
52
+ request.reported[field] = true;
53
+ if (field === 'input')
54
+ accounting.lastInputTokens = value;
55
+ }
56
+ accounting.inputReportedRequests += Number(request.reported.input) - Number(hadInput);
57
+ accounting.outputReportedRequests += Number(request.reported.output) - Number(hadOutput);
58
+ const hasPair = validCachePair(request);
59
+ accounting.cacheReportedRequests += Number(hasPair) - Number(hadPair);
60
+ accounting.cacheEligibleInputTokens += (hasPair ? request.usage.input : 0) - previousEligibleInput;
61
+ accounting.cacheReadTokens += (hasPair ? request.usage.cacheRead : 0) - previousEligibleCache;
62
+ }
63
+ function ratioSummary(accounting) {
64
+ const denominator = accounting.cacheEligibleInputTokens;
65
+ const knownRatio = denominator > 0 ? accounting.cacheReadTokens / denominator : null;
66
+ const complete = !accounting.hasLegacyTotals && accounting.requests > 0
67
+ && accounting.inputReportedRequests === accounting.requests
68
+ && accounting.cacheReportedRequests === accounting.requests;
69
+ return { totalRatio: complete ? knownRatio : null, knownRatio, denominator };
70
+ }
71
+ //# sourceMappingURL=providerUsageAccounting.js.map
@@ -0,0 +1,18 @@
1
+ /** A local estimate of the actual submitted prompt, never a billing/tokenizer claim. */
2
+ export interface RequestContextEstimate {
3
+ requestId: string;
4
+ runId: string;
5
+ model: string;
6
+ at: string;
7
+ estimatedTokens: number;
8
+ longHistoryTokens: number;
9
+ buildBlockTokens: number;
10
+ systemPromptTokens: number;
11
+ toolSchemaTokens: number;
12
+ hasImages: boolean;
13
+ messageCount: number;
14
+ inputTokens?: number;
15
+ cacheReadTokens?: number;
16
+ }
17
+ export declare function estimateSubmittedContext(messages: Array<Record<string, unknown>>, systemPrompt: string, tools: unknown[], buildBlockStart: number): Omit<RequestContextEstimate, 'requestId' | 'runId' | 'model' | 'at'>;
18
+ //# sourceMappingURL=requestContextEstimate.d.ts.map
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.estimateSubmittedContext = estimateSubmittedContext;
4
+ function textTokens(text) {
5
+ const nonAscii = text.length - text.replace(/[\u0080-\uFFFF]/g, '').length;
6
+ return Math.ceil((text.length - nonAscii) / 4 + nonAscii);
7
+ }
8
+ function estimateSubmittedContext(messages, systemPrompt, tools, buildBlockStart) {
9
+ let longHistoryTokens = 0;
10
+ let buildBlockTokens = 0;
11
+ let hasImages = false;
12
+ const boundary = Math.max(0, Math.min(messages.length, buildBlockStart));
13
+ for (let index = 0; index < messages.length; index++) {
14
+ const message = messages[index];
15
+ let content = '';
16
+ if (typeof message.content === 'string')
17
+ content = message.content;
18
+ else if (Array.isArray(message.content)) {
19
+ for (const item of message.content) {
20
+ if (!item || typeof item !== 'object')
21
+ continue;
22
+ const part = item;
23
+ if (part.type === 'image_url' || part.type === 'image' || part.type === 'input_image') {
24
+ // Image tokens depend on provider/model/resolution. Base64 size is
25
+ // not token usage; expose incomplete local estimation explicitly.
26
+ hasImages = true;
27
+ }
28
+ else if (typeof part.text === 'string')
29
+ content += part.text;
30
+ else
31
+ content += JSON.stringify(part);
32
+ }
33
+ }
34
+ else if (message.content)
35
+ content = JSON.stringify(message.content);
36
+ const calls = Array.isArray(message.tool_calls) ? JSON.stringify(message.tool_calls) : '';
37
+ const count = 6 + textTokens(content) + textTokens(calls)
38
+ + (calls ? Math.ceil(calls.length / 6) : 0)
39
+ + textTokens(String(message.name || '') + String(message.tool_call_id || ''));
40
+ if (index < boundary)
41
+ longHistoryTokens += count;
42
+ else
43
+ buildBlockTokens += count;
44
+ }
45
+ const systemPromptTokens = systemPrompt ? 6 + textTokens(systemPrompt) : 0;
46
+ const toolText = tools.length ? JSON.stringify(tools) : '';
47
+ const toolSchemaTokens = toolText ? textTokens(toolText) + Math.ceil(toolText.length / 6) : 0;
48
+ return {
49
+ estimatedTokens: longHistoryTokens + buildBlockTokens + systemPromptTokens + toolSchemaTokens,
50
+ longHistoryTokens, buildBlockTokens, systemPromptTokens, toolSchemaTokens,
51
+ hasImages, messageCount: messages.length,
52
+ };
53
+ }
54
+ //# sourceMappingURL=requestContextEstimate.js.map
@@ -2,6 +2,7 @@ import { NewmarkToolResult } from './compat';
2
2
  import type { AgentMode } from './types';
3
3
  export type SubagentStatus = 'idle' | 'queued' | 'working' | 'completed' | 'closed' | 'error';
4
4
  export type SubagentMessageKind = 'directive' | 'question' | 'result' | 'handoff';
5
+ export declare const SUBAGENT_RECOVERY_PROMPT = "[Peer runtime recovery] Continue the unfinished work from the persisted working history. Preserve completed tool results and follow the latest instruction; do not repeat completed work.";
5
6
  export interface SubagentMessage {
6
7
  id: string;
7
8
  conversationId: string;
@@ -10,6 +11,15 @@ export interface SubagentMessage {
10
11
  toAgentId: string;
11
12
  kind: SubagentMessageKind;
12
13
  body: string;
14
+ wakeup: boolean;
15
+ /** A message accepted by an existing job must survive that job settling. */
16
+ acceptedWhileActive: boolean;
17
+ /** Runtime-consumed control messages never become model continuation input. */
18
+ control?: {
19
+ action: 'stop';
20
+ runId: string;
21
+ force: boolean;
22
+ };
13
23
  correlationId?: string;
14
24
  replyTo?: string;
15
25
  createdAt: string;
@@ -23,8 +33,16 @@ export interface SubagentRootMessage {
23
33
  toAgentId: string;
24
34
  kind: SubagentMessageKind;
25
35
  body: string;
36
+ wakeup: boolean;
26
37
  createdAt: string;
27
38
  readAt?: string;
39
+ /** Only runtime-generated completion notices carry this provenance. */
40
+ source?: 'automatic-settlement';
41
+ settlementRevision?: number;
42
+ }
43
+ export interface SubagentSettlementReceipt {
44
+ peerId: string;
45
+ revision: number;
28
46
  }
29
47
  export interface SubagentMessageRecord {
30
48
  role: string;
@@ -69,6 +87,8 @@ export interface SubagentInstance {
69
87
  queueSequence?: number;
70
88
  messages: Array<SubagentMessageRecord>;
71
89
  result: string | null;
90
+ /** Monotonic per-peer result version, independent of timestamps/content. */
91
+ settlementRevision?: number;
72
92
  error?: string;
73
93
  createdAt: string;
74
94
  startedAt?: string;
@@ -94,6 +114,8 @@ export interface SubagentState {
94
114
  nextSequence: number;
95
115
  /** Cooperative root-run stop gate. Pending peers stay durable but cannot start until the next root run resumes scheduling. */
96
116
  schedulingPaused?: boolean;
117
+ /** Exact queued/in-flight inputs survive a restart between mailbox dispatch and child transcript persistence. */
118
+ jobs?: PendingJob[];
97
119
  records: SubagentInstance[];
98
120
  mailbox: SubagentMessage[];
99
121
  rootInbox: SubagentRootMessage[];
@@ -142,7 +164,7 @@ export interface SubagentReadSnapshot {
142
164
  inbound: number;
143
165
  outbound: number;
144
166
  unread: number;
145
- latest: Array<Pick<SubagentMessage, 'id' | 'sequence' | 'fromAgentId' | 'toAgentId' | 'kind' | 'body' | 'createdAt' | 'readAt'>>;
167
+ latest: Array<Pick<SubagentMessage, 'id' | 'sequence' | 'fromAgentId' | 'toAgentId' | 'kind' | 'body' | 'wakeup' | 'acceptedWhileActive' | 'createdAt' | 'readAt'>>;
146
168
  };
147
169
  truncated: boolean;
148
170
  }
@@ -167,6 +189,14 @@ export interface SubagentManagerOptions {
167
189
  onRootInboxMessage?: (message: SubagentRootMessage) => boolean;
168
190
  onSettled?: (record: SubagentInstance) => void;
169
191
  }
192
+ interface PendingJob {
193
+ id: string;
194
+ prompt: string;
195
+ flowName: string;
196
+ sequence: number;
197
+ reason: SubagentExecutionJob['reason'];
198
+ inputCommitted?: boolean;
199
+ }
170
200
  /** Conversation-owned flat peer-agent coordinator with durable FIFO scheduling. */
171
201
  export declare class SubagentManager {
172
202
  private subs;
@@ -174,6 +204,7 @@ export declare class SubagentManager {
174
204
  private rootInbox;
175
205
  private pending;
176
206
  private running;
207
+ private activeJobs;
177
208
  private schedulingPaused;
178
209
  private nextSequence;
179
210
  private concurrency;
@@ -182,33 +213,46 @@ export declare class SubagentManager {
182
213
  private persist?;
183
214
  private onMailboxMessage?;
184
215
  private rootInboxListeners;
216
+ private bindingOwner?;
217
+ private ownerBindings;
185
218
  private onSettled?;
186
219
  private changedQueued;
187
220
  private settledWaiters;
188
221
  readonly conversationId: string;
189
222
  readonly rootAgentId: string;
190
223
  hasRecords(): boolean;
224
+ hasPendingWork(): boolean;
191
225
  reset(): void;
192
226
  constructor(options?: SubagentManagerOptions);
193
227
  bind(options: Pick<SubagentManagerOptions, 'concurrency' | 'executor' | 'onChange' | 'persist' | 'onMailboxMessage' | 'onRootInboxMessage' | 'onSettled'>): void;
228
+ ownsExecutionBinding(listener: (message: SubagentRootMessage) => boolean): boolean;
229
+ private rememberOwnerBinding;
194
230
  removeRootInboxListener(listener: (message: SubagentRootMessage) => boolean): void;
231
+ /** Detach one facade without clearing a newer owner's callbacks or live jobs. */
232
+ releaseOwnerBinding(listener: (message: SubagentRootMessage) => boolean): boolean;
195
233
  create(name: string, prompt: string, model?: string, inputMode?: string, agentMode?: AgentMode, createdByAgentId?: string, flowName?: string, goalObjective?: string, flowPc?: number, buildRunId?: string, intelligenceTier?: string): string;
196
234
  get(id: string): SubagentInstance | undefined;
197
- send(id: string, prompt: string): boolean;
235
+ send(id: string, prompt: string, wakeup?: boolean): boolean;
236
+ broadcastStop(fromAgentId: string, runId: string, force?: boolean): {
237
+ count: number;
238
+ ids: string[];
239
+ peerIds: string[];
240
+ };
198
241
  sendMessage(fromAgentId: string, toAgentId: string, body: string, kind?: SubagentMessageKind, details?: {
199
242
  correlationId?: string;
200
243
  replyTo?: string;
201
- }): {
244
+ }, wakeup?: boolean): {
202
245
  ok: boolean;
203
246
  message?: SubagentMessage;
204
247
  error?: string;
205
248
  };
206
- sendRootMessage(fromAgentId: string, body: string, kind?: SubagentMessageKind): {
249
+ sendRootMessage(fromAgentId: string, body: string, kind?: SubagentMessageKind, settlementRevision?: number, wakeup?: boolean): {
207
250
  ok: boolean;
208
251
  message?: SubagentRootMessage;
209
252
  error?: string;
210
253
  };
211
254
  readRootInbox(): SubagentRootMessage[];
255
+ acknowledgeSettlementResults(receipts: SubagentSettlementReceipt[]): string[];
212
256
  acknowledgeRootInbox(messageId: string): boolean;
213
257
  consumeMailbox(agentId: string): SubagentMessage[];
214
258
  acknowledgeMailbox(agentId: string, messageId: string): boolean;
@@ -219,7 +263,8 @@ export declare class SubagentManager {
219
263
  error?: string;
220
264
  };
221
265
  appendAssistant(id: string, content: string): void;
222
- replaceContext(id: string, history: Array<Record<string, unknown>>, compression: SubagentCompressionState | null): void;
266
+ replaceContext(id: string, history: Array<Record<string, unknown>>, compression: SubagentCompressionState | null, inputCommitted?: boolean): void;
267
+ patchMetadata(id: string, patch: Record<string, unknown>): void;
223
268
  complete(id: string, result: string): void;
224
269
  fail(id: string, error: string): void;
225
270
  markWorking(id: string): void;
@@ -235,6 +280,44 @@ export declare class SubagentManager {
235
280
  boundedResultTranscript(idOrName: string): string;
236
281
  listActive(): SubagentInstance[];
237
282
  listAll(): SubagentInstance[];
283
+ /** Model-facing list: detailed transcripts and request cache remain private. */
284
+ listSummaries(status?: string): {
285
+ id: string;
286
+ shortId: string;
287
+ natureSlug: string;
288
+ displayName: string;
289
+ qualifiedName: string;
290
+ name: string;
291
+ conversationId: string;
292
+ createdByAgentId: string;
293
+ buildRunId: string | undefined;
294
+ intelligenceTier: string | undefined;
295
+ prompt: string;
296
+ model: string;
297
+ inputMode: string;
298
+ agentMode: AgentMode;
299
+ mode: AgentMode;
300
+ status: SubagentStatus;
301
+ active: boolean;
302
+ goalObjective: string | undefined;
303
+ flowName: string | undefined;
304
+ flowPc: number | undefined;
305
+ queueSequence: number | undefined;
306
+ resultAvailable: boolean;
307
+ resultChars: number;
308
+ settlementRevision: number | undefined;
309
+ messageCount: number;
310
+ error: string | undefined;
311
+ createdAt: string;
312
+ startedAt: string | undefined;
313
+ updatedAt: string;
314
+ completedAt: string | undefined;
315
+ closedAt: string | undefined;
316
+ mailbox: {
317
+ total: number;
318
+ unread: number;
319
+ };
320
+ }[];
238
321
  activeCountForBuild(buildRunId: string): number;
239
322
  setConcurrencyLimit(value: number): void;
240
323
  concurrencyLimit(): number;
@@ -258,4 +341,5 @@ export declare class SubagentManager {
258
341
  }
259
342
  export declare function sharedSubagentManager(key: string, options: SubagentManagerOptions): SubagentManager;
260
343
  export declare function releaseSharedSubagentManager(key: string): void;
344
+ export {};
261
345
  //# sourceMappingURL=subagent.d.ts.map