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.
- package/dist/conversation-utility-host.bundle.cjs +3373 -1900
- package/dist/conversation-utility-host.js +1 -1
- package/dist/core/agent.d.ts +105 -14
- package/dist/core/agent.js +658 -104
- 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 +236 -85
- package/dist/core/autoRouter.js +16 -3
- 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 +347 -83
- 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/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 +32 -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 +148 -267
- 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 +2374 -616
- package/dist/ui/lucide-sprite.svg +7 -0
- package/dist/ui/startup.html +6 -6
- package/dist/wsl-agent-host.bundle.cjs +3381 -1906
- package/dist/wsl-agent-host.js +9 -4
- 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(
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
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
|
|
668
|
+
exitCode,
|
|
645
669
|
logPath,
|
|
646
|
-
error: ok ? undefined :
|
|
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
|
-
|
|
756
|
-
|
|
757
|
-
|
|
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
|
package/dist/core/subagent.d.ts
CHANGED
|
@@ -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
|