@cjhyy/code-shell-core 0.7.0 → 0.7.1
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/arena/arena.d.ts +2 -0
- package/dist/arena/arena.js +30 -1
- package/dist/arena/phases/adjudication.d.ts +2 -1
- package/dist/arena/phases/adjudication.js +2 -1
- package/dist/arena/phases/build-consensus.d.ts +2 -1
- package/dist/arena/phases/build-consensus.js +3 -1
- package/dist/arena/phases/cross-review.d.ts +3 -1
- package/dist/arena/phases/cross-review.js +8 -2
- package/dist/arena/phases/debate-rounds.d.ts +2 -1
- package/dist/arena/phases/debate-rounds.js +4 -2
- package/dist/arena/phases/participant-research.d.ts +2 -1
- package/dist/arena/phases/participant-research.js +3 -1
- package/dist/arena/phases/planning-detail-expansion.d.ts +2 -1
- package/dist/arena/phases/planning-detail-expansion.js +2 -1
- package/dist/arena/planner.d.ts +2 -1
- package/dist/arena/planner.js +2 -1
- package/dist/arena/types.d.ts +5 -1
- package/dist/cli/agent-server-tcp.js +13 -2
- package/dist/context/manager.d.ts +3 -3
- package/dist/context/manager.js +6 -6
- package/dist/context/token-counter.js +5 -3
- package/dist/context/tool-result-storage.d.ts +6 -0
- package/dist/context/tool-result-storage.js +25 -4
- package/dist/credentials/access.d.ts +10 -1
- package/dist/credentials/access.js +76 -2
- package/dist/credentials/index.d.ts +3 -2
- package/dist/credentials/index.js +2 -1
- package/dist/credentials/oauth.d.ts +6 -1
- package/dist/credentials/oauth.js +66 -1
- package/dist/credentials/store.d.ts +1 -1
- package/dist/credentials/store.js +16 -7
- package/dist/credentials/types.d.ts +38 -1
- package/dist/credentials/types.js +16 -1
- package/dist/engine/engine.d.ts +61 -32
- package/dist/engine/engine.js +289 -55
- package/dist/engine/goal.d.ts +5 -3
- package/dist/engine/session-title.d.ts +2 -1
- package/dist/engine/session-title.js +4 -1
- package/dist/engine/turn-loop.d.ts +18 -0
- package/dist/engine/turn-loop.js +202 -27
- package/dist/engine/types.d.ts +6 -2
- package/dist/git/worktree/crud.d.ts +3 -0
- package/dist/git/worktree/crud.js +32 -3
- package/dist/git/worktree/git-exec.d.ts +2 -2
- package/dist/git/worktree/git-exec.js +47 -11
- package/dist/git/worktree/query.d.ts +8 -7
- package/dist/git/worktree/query.js +27 -20
- package/dist/hooks/events.d.ts +3 -0
- package/dist/hooks/events.js +0 -3
- package/dist/hooks/goal-stop-hook.d.ts +14 -4
- package/dist/hooks/goal-stop-hook.js +626 -71
- package/dist/hooks/registry.js +3 -0
- package/dist/hooks/shell-runner.d.ts +12 -1
- package/dist/hooks/shell-runner.js +160 -9
- package/dist/index.d.ts +7 -6
- package/dist/index.js +6 -5
- package/dist/llm/client-base.js +12 -10
- package/dist/llm/types.d.ts +12 -5
- package/dist/plugins/pluginCommandHook.d.ts +4 -4
- package/dist/plugins/pluginCommandHook.js +111 -13
- package/dist/protocol/chat-session-manager.d.ts +13 -2
- package/dist/protocol/chat-session-manager.js +90 -18
- package/dist/protocol/chat-session.d.ts +12 -0
- package/dist/protocol/chat-session.js +30 -5
- package/dist/protocol/client.d.ts +5 -2
- package/dist/protocol/client.js +22 -1
- package/dist/protocol/server.d.ts +25 -11
- package/dist/protocol/server.js +290 -72
- package/dist/protocol/types.d.ts +34 -2
- package/dist/protocol/types.js +2 -0
- package/dist/services/dream-consolidation.d.ts +3 -0
- package/dist/services/dream-consolidation.js +4 -1
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/oauth.d.ts +34 -10
- package/dist/services/oauth.js +233 -98
- package/dist/session/session-manager.d.ts +35 -6
- package/dist/session/session-manager.js +362 -26
- package/dist/session/transcript.d.ts +30 -1
- package/dist/session/transcript.js +119 -4
- package/dist/tool-system/builtin/agent.js +5 -1
- package/dist/tool-system/builtin/arena.js +1 -0
- package/dist/tool-system/builtin/background-jobs.d.ts +8 -5
- package/dist/tool-system/builtin/background-jobs.js +15 -11
- package/dist/tool-system/builtin/background-work.d.ts +6 -1
- package/dist/tool-system/builtin/background-work.js +5 -1
- package/dist/tool-system/builtin/bash.d.ts +3 -5
- package/dist/tool-system/builtin/bash.js +10 -5
- package/dist/tool-system/builtin/browser-tools.d.ts +2 -2
- package/dist/tool-system/builtin/edit.d.ts +2 -1
- package/dist/tool-system/builtin/edit.js +12 -4
- package/dist/tool-system/builtin/generate-video.d.ts +4 -0
- package/dist/tool-system/builtin/generate-video.js +138 -21
- package/dist/tool-system/builtin/glob.d.ts +2 -1
- package/dist/tool-system/builtin/glob.js +28 -3
- package/dist/tool-system/builtin/grep.d.ts +1 -0
- package/dist/tool-system/builtin/grep.js +82 -17
- package/dist/tool-system/builtin/index.d.ts +25 -11
- package/dist/tool-system/builtin/index.js +45 -2
- package/dist/tool-system/builtin/video-providers.d.ts +12 -15
- package/dist/tool-system/builtin/video-providers.js +1 -0
- package/dist/tool-system/builtin/view-image.d.ts +2 -2
- package/dist/tool-system/builtin/web-fetch.js +44 -3
- package/dist/tool-system/builtin/worktree.js +25 -7
- package/dist/tool-system/builtin/write.d.ts +2 -1
- package/dist/tool-system/builtin/write.js +14 -4
- package/dist/tool-system/context.d.ts +17 -5
- package/dist/tool-system/executor.js +24 -8
- package/dist/tool-system/mcp-manager.d.ts +6 -0
- package/dist/tool-system/mcp-manager.js +55 -5
- package/dist/tool-system/path-policy.d.ts +19 -0
- package/dist/tool-system/path-policy.js +62 -1
- package/dist/tool-system/permission.d.ts +43 -3
- package/dist/tool-system/permission.js +383 -30
- package/dist/tool-system/registry.d.ts +3 -2
- package/dist/tool-system/registry.js +52 -34
- package/dist/types.d.ts +15 -0
- package/package.json +1 -1
package/dist/arena/arena.d.ts
CHANGED
package/dist/arena/arena.js
CHANGED
|
@@ -28,6 +28,7 @@ import { runDetailExpansion } from "./phases/planning-detail-expansion.js";
|
|
|
28
28
|
import { withLanguage } from "./strategies/language-wrapper.js";
|
|
29
29
|
import { ArenaLedger } from "./ledger.js";
|
|
30
30
|
import { registerClaims } from "./phases/claim-registry.js";
|
|
31
|
+
import { addTokenUsage } from "../engine/session-usage.js";
|
|
31
32
|
/** Bucket claims by status. Shared by the two consensus phases. */
|
|
32
33
|
function summarizeClaims(all) {
|
|
33
34
|
return {
|
|
@@ -41,6 +42,17 @@ export class Arena {
|
|
|
41
42
|
config;
|
|
42
43
|
strategy;
|
|
43
44
|
limits;
|
|
45
|
+
usage = {
|
|
46
|
+
promptTokens: 0,
|
|
47
|
+
completionTokens: 0,
|
|
48
|
+
totalTokens: 0,
|
|
49
|
+
cacheReadTokens: 0,
|
|
50
|
+
cacheCreationTokens: 0,
|
|
51
|
+
};
|
|
52
|
+
recordUsage = (usage) => {
|
|
53
|
+
if (usage)
|
|
54
|
+
this.usage = addTokenUsage(this.usage, usage);
|
|
55
|
+
};
|
|
44
56
|
constructor(config) {
|
|
45
57
|
if (config.participants.length < 2) {
|
|
46
58
|
throw new Error("Arena requires at least 2 participants");
|
|
@@ -63,13 +75,20 @@ export class Arena {
|
|
|
63
75
|
* @param flags - Optional explicit overrides (mode, base, head)
|
|
64
76
|
*/
|
|
65
77
|
async run(topic, flags) {
|
|
78
|
+
this.usage = {
|
|
79
|
+
promptTokens: 0,
|
|
80
|
+
completionTokens: 0,
|
|
81
|
+
totalTokens: 0,
|
|
82
|
+
cacheReadTokens: 0,
|
|
83
|
+
cacheCreationTokens: 0,
|
|
84
|
+
};
|
|
66
85
|
const participantNames = this.config.participants.map((p) => p.name);
|
|
67
86
|
const signal = this.config.signal;
|
|
68
87
|
logger.info("arena.start", { mode: this.config.mode, participants: participantNames });
|
|
69
88
|
signal?.throwIfAborted();
|
|
70
89
|
// ─── Phase 0: Plan ────────────────────────────────────────
|
|
71
90
|
const llmConfig = this.config.participants[0].llm;
|
|
72
|
-
const plan = await planArena(topic, llmConfig, flags, signal);
|
|
91
|
+
const plan = await planArena(topic, llmConfig, flags, signal, this.recordUsage);
|
|
73
92
|
this.config.onProgress?.({ type: "plan_resolved", plan });
|
|
74
93
|
logger.info("arena.plan", {
|
|
75
94
|
mode: plan.mode,
|
|
@@ -129,6 +148,7 @@ export class Arena {
|
|
|
129
148
|
contextTools,
|
|
130
149
|
signal,
|
|
131
150
|
onProgress: this.config.onProgress,
|
|
151
|
+
onUsage: this.recordUsage,
|
|
132
152
|
});
|
|
133
153
|
const reports = researchResults.map((r) => r.report);
|
|
134
154
|
const dossiers = researchResults.map((r) => r.dossier);
|
|
@@ -197,6 +217,7 @@ export class Arena {
|
|
|
197
217
|
mode: "planning",
|
|
198
218
|
signal,
|
|
199
219
|
onProgress: this.config.onProgress,
|
|
220
|
+
onUsage: this.recordUsage,
|
|
200
221
|
});
|
|
201
222
|
signal?.throwIfAborted();
|
|
202
223
|
// ─── Roadmap Consensus ───────────────────────────────────
|
|
@@ -212,6 +233,7 @@ export class Arena {
|
|
|
212
233
|
claimSummary,
|
|
213
234
|
signal,
|
|
214
235
|
onProgress: this.config.onProgress,
|
|
236
|
+
onUsage: this.recordUsage,
|
|
215
237
|
});
|
|
216
238
|
signal?.throwIfAborted();
|
|
217
239
|
// ─── Detail Expansion ────────────────────────────────────
|
|
@@ -229,6 +251,7 @@ export class Arena {
|
|
|
229
251
|
contextTools,
|
|
230
252
|
signal,
|
|
231
253
|
onProgress: this.config.onProgress,
|
|
254
|
+
onUsage: this.recordUsage,
|
|
232
255
|
});
|
|
233
256
|
consensus.roadmapDetails = details;
|
|
234
257
|
}
|
|
@@ -251,6 +274,7 @@ export class Arena {
|
|
|
251
274
|
claims: allClaims,
|
|
252
275
|
reviews,
|
|
253
276
|
consensus,
|
|
277
|
+
usage: { ...this.usage },
|
|
254
278
|
};
|
|
255
279
|
}
|
|
256
280
|
/**
|
|
@@ -271,6 +295,7 @@ export class Arena {
|
|
|
271
295
|
limits: this.limits,
|
|
272
296
|
signal,
|
|
273
297
|
onProgress: this.config.onProgress,
|
|
298
|
+
onUsage: this.recordUsage,
|
|
274
299
|
});
|
|
275
300
|
signal?.throwIfAborted();
|
|
276
301
|
// ─── Debate Rounds ───────────────────────────────────────
|
|
@@ -284,6 +309,7 @@ export class Arena {
|
|
|
284
309
|
maxRounds: this.config.maxDiscussionRounds,
|
|
285
310
|
signal,
|
|
286
311
|
onProgress: this.config.onProgress,
|
|
312
|
+
onUsage: this.recordUsage,
|
|
287
313
|
});
|
|
288
314
|
signal?.throwIfAborted();
|
|
289
315
|
// ─── Adjudication ────────────────────────────────────────
|
|
@@ -295,6 +321,7 @@ export class Arena {
|
|
|
295
321
|
ledger,
|
|
296
322
|
signal,
|
|
297
323
|
onProgress: this.config.onProgress,
|
|
324
|
+
onUsage: this.recordUsage,
|
|
298
325
|
});
|
|
299
326
|
signal?.throwIfAborted();
|
|
300
327
|
// ─── Consensus (claim-aware) ─────────────────────────────
|
|
@@ -314,6 +341,7 @@ export class Arena {
|
|
|
314
341
|
claimSummary,
|
|
315
342
|
signal,
|
|
316
343
|
onProgress: this.config.onProgress,
|
|
344
|
+
onUsage: this.recordUsage,
|
|
317
345
|
});
|
|
318
346
|
return {
|
|
319
347
|
topic,
|
|
@@ -328,6 +356,7 @@ export class Arena {
|
|
|
328
356
|
debateRounds,
|
|
329
357
|
adjudications,
|
|
330
358
|
consensus,
|
|
359
|
+
usage: { ...this.usage },
|
|
331
360
|
};
|
|
332
361
|
}
|
|
333
362
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Claims that were verified without contest get synthetic adjudication records.
|
|
8
8
|
*/
|
|
9
|
-
import type { ArenaParticipant, ArenaStrategy, ClaimAdjudication, ArenaProgressEvent } from "../types.js";
|
|
9
|
+
import type { ArenaParticipant, ArenaStrategy, ClaimAdjudication, ArenaProgressEvent, ArenaUsageRecorder } from "../types.js";
|
|
10
10
|
import type { ArenaLedger } from "../ledger.js";
|
|
11
11
|
interface AdjudicationOptions {
|
|
12
12
|
concluder: ArenaParticipant;
|
|
@@ -15,6 +15,7 @@ interface AdjudicationOptions {
|
|
|
15
15
|
ledger: ArenaLedger;
|
|
16
16
|
signal?: AbortSignal;
|
|
17
17
|
onProgress?: (event: ArenaProgressEvent) => void;
|
|
18
|
+
onUsage?: ArenaUsageRecorder;
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* Run adjudication phase. The moderator rules on contested claims.
|
|
@@ -17,7 +17,7 @@ import { logger } from "../../logging/logger.js";
|
|
|
17
17
|
* Verified claims get synthetic adjudications.
|
|
18
18
|
*/
|
|
19
19
|
export async function runAdjudication(options) {
|
|
20
|
-
const { concluder, strategy, topic, ledger, signal, onProgress } = options;
|
|
20
|
+
const { concluder, strategy, topic, ledger, signal, onProgress, onUsage } = options;
|
|
21
21
|
const allAdjudications = [];
|
|
22
22
|
const v2 = isStrategyV2(strategy);
|
|
23
23
|
// Claims that need moderator adjudication: contested or those with debate rounds
|
|
@@ -54,6 +54,7 @@ export async function runAdjudication(options) {
|
|
|
54
54
|
messages: [{ role: "user", content: userContent }],
|
|
55
55
|
signal,
|
|
56
56
|
});
|
|
57
|
+
onUsage?.(response.usage);
|
|
57
58
|
logger.info("arena.adjudication_response", {
|
|
58
59
|
claimId: claim.claimId,
|
|
59
60
|
stopReason: response.stopReason,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* In both paths, the moderator must faithfully reflect aggregated results
|
|
9
9
|
* (no new unsourced conclusions).
|
|
10
10
|
*/
|
|
11
|
-
import type { ArenaParticipant, ArenaStrategy, ParticipantReport, FindingReview, ArenaConsensus, ClaimStatusSummary, ArenaProgressEvent } from "../types.js";
|
|
11
|
+
import type { ArenaParticipant, ArenaStrategy, ParticipantReport, FindingReview, ArenaConsensus, ClaimStatusSummary, ArenaProgressEvent, ArenaUsageRecorder } from "../types.js";
|
|
12
12
|
interface ConsensusOptions {
|
|
13
13
|
/** The participant that acts as moderator/concluder */
|
|
14
14
|
concluder: ArenaParticipant;
|
|
@@ -20,6 +20,7 @@ interface ConsensusOptions {
|
|
|
20
20
|
claimSummary?: ClaimStatusSummary;
|
|
21
21
|
signal?: AbortSignal;
|
|
22
22
|
onProgress?: (event: ArenaProgressEvent) => void;
|
|
23
|
+
onUsage?: ArenaUsageRecorder;
|
|
23
24
|
}
|
|
24
25
|
/**
|
|
25
26
|
* Build structured consensus from participant reports, cross-reviews,
|
|
@@ -16,7 +16,7 @@ import { isStrategyV2 } from "../types.js";
|
|
|
16
16
|
* and optionally adjudicated claim data.
|
|
17
17
|
*/
|
|
18
18
|
export async function buildConsensus(options) {
|
|
19
|
-
const { concluder, strategy, topic, reports, reviews, claimSummary, signal, onProgress } = options;
|
|
19
|
+
const { concluder, strategy, topic, reports, reviews, claimSummary, signal, onProgress, onUsage } = options;
|
|
20
20
|
onProgress?.({ type: "consensus_start" });
|
|
21
21
|
const client = await createLLMClient(concluder.llm, concluder.clientDefaults);
|
|
22
22
|
const systemPrompt = strategy.consensusSystemPrompt();
|
|
@@ -41,6 +41,7 @@ export async function buildConsensus(options) {
|
|
|
41
41
|
messages: [{ role: "user", content: userContent }],
|
|
42
42
|
signal,
|
|
43
43
|
});
|
|
44
|
+
onUsage?.(response.usage);
|
|
44
45
|
logger.info("arena.consensus_raw_response", {
|
|
45
46
|
text: response.text,
|
|
46
47
|
stopReason: response.stopReason,
|
|
@@ -64,6 +65,7 @@ export async function buildConsensus(options) {
|
|
|
64
65
|
],
|
|
65
66
|
signal,
|
|
66
67
|
});
|
|
68
|
+
onUsage?.(retryResponse.usage);
|
|
67
69
|
logger.info("arena.consensus_retry_response", {
|
|
68
70
|
textLen: retryResponse.text?.length ?? 0,
|
|
69
71
|
stopReason: retryResponse.stopReason,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* V2 (VerificationReview): Reviewers get claim + evidence packet + digest data,
|
|
8
8
|
* and produce ClaimChallenge records that feed into the claim state machine.
|
|
9
9
|
*/
|
|
10
|
-
import type { ArenaParticipant, ArenaStrategy, ParticipantReport, FindingReview, ClaimChallenge, ArenaProgressEvent, ArenaExecutionLimits } from "../types.js";
|
|
10
|
+
import type { ArenaParticipant, ArenaStrategy, ParticipantReport, FindingReview, ClaimChallenge, ArenaProgressEvent, ArenaExecutionLimits, ArenaUsageRecorder } from "../types.js";
|
|
11
11
|
import type { ArenaLedger } from "../ledger.js";
|
|
12
12
|
interface CrossReviewOptions {
|
|
13
13
|
participants: ArenaParticipant[];
|
|
@@ -16,6 +16,7 @@ interface CrossReviewOptions {
|
|
|
16
16
|
reports: ParticipantReport[];
|
|
17
17
|
signal?: AbortSignal;
|
|
18
18
|
onProgress?: (event: ArenaProgressEvent) => void;
|
|
19
|
+
onUsage?: ArenaUsageRecorder;
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
21
22
|
* Run cross-review phase (V1). Each participant reviews the others' findings.
|
|
@@ -32,6 +33,7 @@ interface VerificationReviewOptions {
|
|
|
32
33
|
mode?: "review" | "discussion" | "planning";
|
|
33
34
|
signal?: AbortSignal;
|
|
34
35
|
onProgress?: (event: ArenaProgressEvent) => void;
|
|
36
|
+
onUsage?: ArenaUsageRecorder;
|
|
35
37
|
}
|
|
36
38
|
interface VerificationReviewResult {
|
|
37
39
|
reviews: FindingReview[];
|
|
@@ -17,7 +17,7 @@ import { logger } from "../../logging/logger.js";
|
|
|
17
17
|
* Run cross-review phase (V1). Each participant reviews the others' findings.
|
|
18
18
|
*/
|
|
19
19
|
export async function runCrossReview(options) {
|
|
20
|
-
const { participants, strategy, topic, reports, signal, onProgress } = options;
|
|
20
|
+
const { participants, strategy, topic, reports, signal, onProgress, onUsage } = options;
|
|
21
21
|
onProgress?.({ type: "cross_review_start", round: 1 });
|
|
22
22
|
const allReviews = [];
|
|
23
23
|
const tasks = participants.map(async (p) => {
|
|
@@ -36,6 +36,7 @@ export async function runCrossReview(options) {
|
|
|
36
36
|
messages: [{ role: "user", content: userContent }],
|
|
37
37
|
signal,
|
|
38
38
|
});
|
|
39
|
+
onUsage?.(response.usage);
|
|
39
40
|
logger.info("arena.cross_review_raw_response", {
|
|
40
41
|
participant: p.name,
|
|
41
42
|
text: response.text,
|
|
@@ -56,6 +57,7 @@ export async function runCrossReview(options) {
|
|
|
56
57
|
],
|
|
57
58
|
signal,
|
|
58
59
|
});
|
|
60
|
+
onUsage?.(retryResponse.usage);
|
|
59
61
|
logger.info("arena.cross_review_retry", {
|
|
60
62
|
participant: p.name,
|
|
61
63
|
stopReason: retryResponse.stopReason,
|
|
@@ -76,7 +78,7 @@ export async function runCrossReview(options) {
|
|
|
76
78
|
* Falls back to V1 cross-review if strategy doesn't implement V2.
|
|
77
79
|
*/
|
|
78
80
|
export async function runVerificationReview(options) {
|
|
79
|
-
const { participants, strategy, topic, reports, ledger, limits, mode, signal, onProgress } = options;
|
|
81
|
+
const { participants, strategy, topic, reports, ledger, limits, mode, signal, onProgress, onUsage } = options;
|
|
80
82
|
// Select claims to review, prioritized by severity + confidence
|
|
81
83
|
const allClaims = ledger.getAllClaims();
|
|
82
84
|
const claimsToReview = selectClaimsForReview(allClaims, limits.maxClaimsForReview);
|
|
@@ -120,6 +122,7 @@ export async function runVerificationReview(options) {
|
|
|
120
122
|
messages: [{ role: "user", content: userContent }],
|
|
121
123
|
signal,
|
|
122
124
|
});
|
|
125
|
+
onUsage?.(response.usage);
|
|
123
126
|
logger.info("arena.verification_review_raw", {
|
|
124
127
|
participant: p.name,
|
|
125
128
|
stopReason: response.stopReason,
|
|
@@ -134,6 +137,7 @@ export async function runVerificationReview(options) {
|
|
|
134
137
|
],
|
|
135
138
|
signal,
|
|
136
139
|
});
|
|
140
|
+
onUsage?.(retryResponse.usage);
|
|
137
141
|
response = retryResponse;
|
|
138
142
|
}
|
|
139
143
|
const challenges = isPlanningMerge
|
|
@@ -163,6 +167,7 @@ export async function runVerificationReview(options) {
|
|
|
163
167
|
messages: [{ role: "user", content: userContent }],
|
|
164
168
|
signal,
|
|
165
169
|
});
|
|
170
|
+
onUsage?.(response.usage);
|
|
166
171
|
if (response.stopReason === "length") {
|
|
167
172
|
const retryResponse = await client.createMessage({
|
|
168
173
|
systemPrompt,
|
|
@@ -173,6 +178,7 @@ export async function runVerificationReview(options) {
|
|
|
173
178
|
],
|
|
174
179
|
signal,
|
|
175
180
|
});
|
|
181
|
+
onUsage?.(retryResponse.usage);
|
|
176
182
|
response = retryResponse;
|
|
177
183
|
}
|
|
178
184
|
const reviews = strategy.parseCrossReviewResponse(p.name, response.text);
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 2. Convergence check: all stances are "support" or "narrow" → resolved
|
|
8
8
|
* 3. If max rounds exhausted → stays contested for adjudication
|
|
9
9
|
*/
|
|
10
|
-
import type { ArenaParticipant, ArenaStrategy, DebateRound, ArenaExecutionLimits, ArenaProgressEvent } from "../types.js";
|
|
10
|
+
import type { ArenaParticipant, ArenaStrategy, DebateRound, ArenaExecutionLimits, ArenaProgressEvent, ArenaUsageRecorder } from "../types.js";
|
|
11
11
|
import type { ArenaLedger } from "../ledger.js";
|
|
12
12
|
interface DebateOptions {
|
|
13
13
|
participants: ArenaParticipant[];
|
|
@@ -18,6 +18,7 @@ interface DebateOptions {
|
|
|
18
18
|
maxRounds: number;
|
|
19
19
|
signal?: AbortSignal;
|
|
20
20
|
onProgress?: (event: ArenaProgressEvent) => void;
|
|
21
|
+
onUsage?: ArenaUsageRecorder;
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
24
|
* Run debate rounds for all contested claims.
|
|
@@ -17,7 +17,7 @@ import { logger } from "../../logging/logger.js";
|
|
|
17
17
|
* Returns empty array if no contested claims exist.
|
|
18
18
|
*/
|
|
19
19
|
export async function runDebateRounds(options) {
|
|
20
|
-
const { participants, strategy, topic, ledger, limits, maxRounds, signal, onProgress } = options;
|
|
20
|
+
const { participants, strategy, topic, ledger, limits, maxRounds, signal, onProgress, onUsage } = options;
|
|
21
21
|
// Select contested claims, capped
|
|
22
22
|
const contested = ledger.getClaimsByStatus("contested")
|
|
23
23
|
.slice(0, limits.maxContestedClaimsForDebate);
|
|
@@ -44,6 +44,7 @@ export async function runDebateRounds(options) {
|
|
|
44
44
|
maxRounds,
|
|
45
45
|
signal,
|
|
46
46
|
onProgress,
|
|
47
|
+
onUsage,
|
|
47
48
|
});
|
|
48
49
|
allDebateRounds.push(...claimDebateRounds);
|
|
49
50
|
// Attach debate rounds to claim record
|
|
@@ -52,7 +53,7 @@ export async function runDebateRounds(options) {
|
|
|
52
53
|
return allDebateRounds;
|
|
53
54
|
}
|
|
54
55
|
async function debateClaim(options) {
|
|
55
|
-
const { claim, participants, strategy, v2, topic, ledger, maxRounds, signal, onProgress } = options;
|
|
56
|
+
const { claim, participants, strategy, v2, topic, ledger, maxRounds, signal, onProgress, onUsage } = options;
|
|
56
57
|
// Identify debaters: claim owner + primary challenger
|
|
57
58
|
const owner = participants.find((p) => p.name === claim.owner);
|
|
58
59
|
const primaryChallenger = findPrimaryChallenger(claim, participants);
|
|
@@ -91,6 +92,7 @@ async function debateClaim(options) {
|
|
|
91
92
|
messages: [{ role: "user", content: userContent }],
|
|
92
93
|
signal,
|
|
93
94
|
});
|
|
95
|
+
onUsage?.(response.usage);
|
|
94
96
|
logger.info("arena.debate_turn", {
|
|
95
97
|
participant: p.name,
|
|
96
98
|
claimId: claim.claimId,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Phase 3 upgrade: now captures ToolTrace and builds EvidencePackets,
|
|
6
6
|
* outputting a full ResearchDossier alongside the ParticipantReport.
|
|
7
7
|
*/
|
|
8
|
-
import type { ArenaBaseContext, ArenaParticipant, ArenaStrategy, ParticipantReport, ResearchDossier, ArenaProgressEvent } from "../types.js";
|
|
8
|
+
import type { ArenaBaseContext, ArenaParticipant, ArenaStrategy, ParticipantReport, ResearchDossier, ArenaProgressEvent, ArenaUsageRecorder } from "../types.js";
|
|
9
9
|
import type { ToolDefinition } from "../../types.js";
|
|
10
10
|
/** Result of a single participant's research — includes both report and dossier */
|
|
11
11
|
export interface ResearchResult {
|
|
@@ -23,6 +23,7 @@ interface ResearchOptions {
|
|
|
23
23
|
/** AbortSignal — cancels in-flight LLM calls */
|
|
24
24
|
signal?: AbortSignal;
|
|
25
25
|
onProgress?: (event: ArenaProgressEvent) => void;
|
|
26
|
+
onUsage?: ArenaUsageRecorder;
|
|
26
27
|
}
|
|
27
28
|
/**
|
|
28
29
|
* Run participant research phase in parallel.
|
|
@@ -23,7 +23,7 @@ export async function runParticipantResearch(options) {
|
|
|
23
23
|
* Returns both reports (backward compat) and dossiers (evidence trail).
|
|
24
24
|
*/
|
|
25
25
|
export async function runParticipantResearchWithDossiers(options) {
|
|
26
|
-
const { participants, strategy, topic, baseContext, enableContextTools, contextTools, signal, onProgress } = options;
|
|
26
|
+
const { participants, strategy, topic, baseContext, enableContextTools, contextTools, signal, onProgress, onUsage } = options;
|
|
27
27
|
const tools = enableContextTools ? (contextTools ?? CONTEXT_TOOLS) : undefined;
|
|
28
28
|
const tasks = participants.map(async (p) => {
|
|
29
29
|
onProgress?.({ type: "research_start", participant: p.name });
|
|
@@ -57,6 +57,7 @@ export async function runParticipantResearchWithDossiers(options) {
|
|
|
57
57
|
tools,
|
|
58
58
|
signal,
|
|
59
59
|
});
|
|
60
|
+
onUsage?.(response.usage);
|
|
60
61
|
const toolNames = (response.toolCalls ?? []).map((tc) => tc.toolName);
|
|
61
62
|
const hasTools = response.toolCalls && response.toolCalls.length > 0;
|
|
62
63
|
logger.info("arena.research_round", {
|
|
@@ -158,6 +159,7 @@ export async function runParticipantResearchWithDossiers(options) {
|
|
|
158
159
|
},
|
|
159
160
|
],
|
|
160
161
|
});
|
|
162
|
+
onUsage?.(forceResponse.usage);
|
|
161
163
|
logger.info("arena.research_force_conclude_response", {
|
|
162
164
|
participant: p.name,
|
|
163
165
|
textLen: forceResponse.text?.length ?? 0,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Tool access is available so the LLM can verify file paths and interfaces.
|
|
11
11
|
*/
|
|
12
|
-
import type { ArenaParticipant, ArenaRoadmapPhase, ArenaRoadmapPhaseDetail, ArenaProgressEvent, ArenaExecutionLimits, ArenaStrategyPlanning } from "../types.js";
|
|
12
|
+
import type { ArenaParticipant, ArenaRoadmapPhase, ArenaRoadmapPhaseDetail, ArenaProgressEvent, ArenaExecutionLimits, ArenaStrategyPlanning, ArenaUsageRecorder } from "../types.js";
|
|
13
13
|
import type { ToolDefinition } from "../../types.js";
|
|
14
14
|
import type { ArenaLedger } from "../ledger.js";
|
|
15
15
|
interface DetailExpansionOptions {
|
|
@@ -27,6 +27,7 @@ interface DetailExpansionOptions {
|
|
|
27
27
|
contextTools?: ToolDefinition[];
|
|
28
28
|
signal?: AbortSignal;
|
|
29
29
|
onProgress?: (event: ArenaProgressEvent) => void;
|
|
30
|
+
onUsage?: ArenaUsageRecorder;
|
|
30
31
|
}
|
|
31
32
|
/**
|
|
32
33
|
* Expand each roadmap phase into a repo-level implementation plan.
|
|
@@ -22,7 +22,7 @@ const MAX_EXPANSION_TOOL_ROUNDS = 3;
|
|
|
22
22
|
* The number of phases expanded is capped by limits.maxExpandedPhasesPerRun.
|
|
23
23
|
*/
|
|
24
24
|
export async function runDetailExpansion(options) {
|
|
25
|
-
const { concluder, strategy, topic, phases, ledger, limits, signal, onProgress } = options;
|
|
25
|
+
const { concluder, strategy, topic, phases, ledger, limits, signal, onProgress, onUsage } = options;
|
|
26
26
|
const tools = options.enableContextTools !== false
|
|
27
27
|
? (options.contextTools ?? CONTEXT_TOOLS)
|
|
28
28
|
: undefined;
|
|
@@ -56,6 +56,7 @@ export async function runDetailExpansion(options) {
|
|
|
56
56
|
tools: offerTools,
|
|
57
57
|
signal,
|
|
58
58
|
});
|
|
59
|
+
onUsage?.(response.usage);
|
|
59
60
|
const hasTools = response.toolCalls && response.toolCalls.length > 0;
|
|
60
61
|
logger.info("arena.detail_expansion_round", {
|
|
61
62
|
phase: phase.title,
|
package/dist/arena/planner.d.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* LLM call that outputs a holistic execution plan.
|
|
13
13
|
*/
|
|
14
14
|
import type { LLMConfig } from "../types.js";
|
|
15
|
+
import type { ArenaUsageRecorder } from "./types.js";
|
|
15
16
|
import type { ArenaPlan, ArenaMode } from "./types.js";
|
|
16
17
|
/** Explicit flags the user may pass via CLI or tool args */
|
|
17
18
|
export interface PlannerFlags {
|
|
@@ -24,4 +25,4 @@ export interface PlannerFlags {
|
|
|
24
25
|
*
|
|
25
26
|
* Priority: explicit flags > LLM plan > safe defaults.
|
|
26
27
|
*/
|
|
27
|
-
export declare function planArena(topic: string, llmConfig: LLMConfig, flags?: PlannerFlags, signal?: AbortSignal): Promise<ArenaPlan>;
|
|
28
|
+
export declare function planArena(topic: string, llmConfig: LLMConfig, flags?: PlannerFlags, signal?: AbortSignal, onUsage?: ArenaUsageRecorder): Promise<ArenaPlan>;
|
package/dist/arena/planner.js
CHANGED
|
@@ -58,7 +58,7 @@ Respond ONLY with JSON, no markdown fences:
|
|
|
58
58
|
*
|
|
59
59
|
* Priority: explicit flags > LLM plan > safe defaults.
|
|
60
60
|
*/
|
|
61
|
-
export async function planArena(topic, llmConfig, flags, signal) {
|
|
61
|
+
export async function planArena(topic, llmConfig, flags, signal, onUsage) {
|
|
62
62
|
// If mode and base are both explicit, we can skip the LLM call
|
|
63
63
|
if (flags?.mode && flags?.base) {
|
|
64
64
|
return buildExplicitPlan(topic, flags);
|
|
@@ -75,6 +75,7 @@ export async function planArena(topic, llmConfig, flags, signal) {
|
|
|
75
75
|
maxTokens: 1024,
|
|
76
76
|
signal,
|
|
77
77
|
});
|
|
78
|
+
onUsage?.(response.usage);
|
|
78
79
|
const plan = parsePlanResponse(response.text, topic);
|
|
79
80
|
// Explicit flags override LLM output
|
|
80
81
|
if (flags?.mode)
|
package/dist/arena/types.d.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* V2 architecture: shared facts → independent research → findings → cross-review → consensus.
|
|
5
5
|
*/
|
|
6
|
-
import type { ClientDefaults, LLMConfig } from "../types.js";
|
|
6
|
+
import type { ClientDefaults, LLMConfig, TokenUsage } from "../types.js";
|
|
7
|
+
/** Receives usage for each completed Arena provider request. */
|
|
8
|
+
export type ArenaUsageRecorder = (usage: TokenUsage | undefined) => void;
|
|
7
9
|
/** Arena operating mode — determines prompts, output format, and default rounds */
|
|
8
10
|
export type ArenaMode = "review" | "discussion" | "planning";
|
|
9
11
|
/** Analysis lens — the perspective from which participants analyze */
|
|
@@ -337,6 +339,8 @@ export interface ArenaResultV2 {
|
|
|
337
339
|
/** Claim adjudications (Phase 3+) */
|
|
338
340
|
adjudications?: ClaimAdjudication[];
|
|
339
341
|
consensus: ArenaConsensus;
|
|
342
|
+
/** Aggregate of every provider request made by this Arena run. */
|
|
343
|
+
usage: TokenUsage;
|
|
340
344
|
}
|
|
341
345
|
/**
|
|
342
346
|
* ArenaStrategy — encapsulates mode-specific behavior for prompts and parsing.
|
|
@@ -33,6 +33,8 @@ import { createRunManager } from "../run/factory.js";
|
|
|
33
33
|
import { startAutomation } from "../automation/index.js";
|
|
34
34
|
import { CronStore, defaultCronStorePath } from "../automation/store.js";
|
|
35
35
|
import { resolveLLMConfigForTag } from "../engine/resolve-llm-config.js";
|
|
36
|
+
import { randomUUID } from "node:crypto";
|
|
37
|
+
import { getApprovalRouter } from "../tool-system/permission.js";
|
|
36
38
|
const cwd = process.env.AGENT_CWD ?? process.cwd();
|
|
37
39
|
const port = Number(process.env.AGENT_TCP_PORT ?? "4321");
|
|
38
40
|
const host = process.env.AGENT_TCP_HOST ?? "127.0.0.1";
|
|
@@ -113,9 +115,18 @@ const automation = startAutomation({
|
|
|
113
115
|
// ── Serve over TCP ──────────────────────────────────────────────
|
|
114
116
|
// One AgentServer per accepted connection, all sharing the same chatManager.
|
|
115
117
|
const servers = new Set();
|
|
116
|
-
listenTcp({ port, host }, (transport) => {
|
|
117
|
-
const server = new AgentServer({
|
|
118
|
+
listenTcp({ port, host }, (transport, socket) => {
|
|
119
|
+
const server = new AgentServer({
|
|
120
|
+
chatManager,
|
|
121
|
+
transport,
|
|
122
|
+
connectionId: randomUUID(),
|
|
123
|
+
approvalRouter: getApprovalRouter(),
|
|
124
|
+
});
|
|
118
125
|
servers.add(server);
|
|
126
|
+
socket.once("close", () => {
|
|
127
|
+
server.disconnect();
|
|
128
|
+
servers.delete(server);
|
|
129
|
+
});
|
|
119
130
|
})
|
|
120
131
|
.then((listener) => {
|
|
121
132
|
process.stderr.write(`[code-shell] automation server listening on ${host}:${listener.port}\n`);
|
|
@@ -30,7 +30,7 @@ export interface ContextManagerConfig {
|
|
|
30
30
|
* Async function type for LLM summarization calls.
|
|
31
31
|
* Injected by the Engine so the ContextManager doesn't depend on LLM directly.
|
|
32
32
|
*/
|
|
33
|
-
export type SummarizeFn = (prompt: string) => Promise<string>;
|
|
33
|
+
export type SummarizeFn = (prompt: string, signal?: AbortSignal) => Promise<string>;
|
|
34
34
|
export type CompactStrategy = "micro" | "summary" | "window" | "snip" | "emergency";
|
|
35
35
|
export type OnCompactFn = (info: {
|
|
36
36
|
strategy: CompactStrategy;
|
|
@@ -111,7 +111,7 @@ export declare class ContextManager {
|
|
|
111
111
|
* Async context management — attempts LLM summarization before falling back.
|
|
112
112
|
* Call this when you have access to the LLM (between turns).
|
|
113
113
|
*/
|
|
114
|
-
manageAsync(messages: Message[]): Promise<Message[]>;
|
|
114
|
+
manageAsync(messages: Message[], signal?: AbortSignal): Promise<Message[]>;
|
|
115
115
|
/**
|
|
116
116
|
* Force maximum compaction, ignoring the ratio gates. This is what a manual
|
|
117
117
|
* `/compact` invokes: the user explicitly asked to shrink NOW, so we don't
|
|
@@ -126,7 +126,7 @@ export declare class ContextManager {
|
|
|
126
126
|
* a long-but-under-threshold text-only conversation (the /compact bug) still
|
|
127
127
|
* gets summarized here.
|
|
128
128
|
*/
|
|
129
|
-
forceSummarize(messages: Message[]): Promise<Message[]>;
|
|
129
|
+
forceSummarize(messages: Message[], signal?: AbortSignal): Promise<Message[]>;
|
|
130
130
|
/**
|
|
131
131
|
* Persist large tool_result blocks to disk and replace them with a
|
|
132
132
|
* preview + filepath. No-op when transcript path hasn't been set
|
package/dist/context/manager.js
CHANGED
|
@@ -162,7 +162,7 @@ export class ContextManager {
|
|
|
162
162
|
setSummarizeFn(fn) {
|
|
163
163
|
this.summarizeFn = fn;
|
|
164
164
|
}
|
|
165
|
-
async trySummaryCompact(messages, before, logEvent) {
|
|
165
|
+
async trySummaryCompact(messages, before, logEvent, signal) {
|
|
166
166
|
if (!this.summarizeFn || this.consecutiveSummaryFailures >= 3) {
|
|
167
167
|
return { messages, tokens: before, compacted: false, noProgress: false };
|
|
168
168
|
}
|
|
@@ -178,7 +178,7 @@ export class ContextManager {
|
|
|
178
178
|
// re-summarizes from scratch.
|
|
179
179
|
const priorSummary = extractAnchoredSummary(messages) ?? this.lastSummary;
|
|
180
180
|
const prompt = buildSummarizationPrompt(messagesToSummarize, priorSummary);
|
|
181
|
-
const summary = await this.summarizeFn(prompt);
|
|
181
|
+
const summary = await this.summarizeFn(prompt, signal);
|
|
182
182
|
if (!summary || summary.length <= 50) {
|
|
183
183
|
this.consecutiveSummaryFailures++;
|
|
184
184
|
logger.warn("context.summary_failed", {
|
|
@@ -358,7 +358,7 @@ export class ContextManager {
|
|
|
358
358
|
* Async context management — attempts LLM summarization before falling back.
|
|
359
359
|
* Call this when you have access to the LLM (between turns).
|
|
360
360
|
*/
|
|
361
|
-
async manageAsync(messages) {
|
|
361
|
+
async manageAsync(messages, signal) {
|
|
362
362
|
let result = messages;
|
|
363
363
|
// Tier 0a: Persist large tool_results to disk + replace with preview.
|
|
364
364
|
result = this.persistLargeToolResults(result);
|
|
@@ -428,7 +428,7 @@ export class ContextManager {
|
|
|
428
428
|
// Tier 2: LLM summary if approaching limit, or if micro was the only tier
|
|
429
429
|
// available in the 0.70-0.85 band and it freed nothing.
|
|
430
430
|
if (ratio >= this.config.compactAtRatio || shouldEscalateNoOpMicro) {
|
|
431
|
-
const summarized = await this.trySummaryCompact(result, tokens, "context.summary_compact");
|
|
431
|
+
const summarized = await this.trySummaryCompact(result, tokens, "context.summary_compact", signal);
|
|
432
432
|
result = summarized.messages;
|
|
433
433
|
tokens = summarized.tokens;
|
|
434
434
|
if (summarized.compacted) {
|
|
@@ -488,7 +488,7 @@ export class ContextManager {
|
|
|
488
488
|
* a long-but-under-threshold text-only conversation (the /compact bug) still
|
|
489
489
|
* gets summarized here.
|
|
490
490
|
*/
|
|
491
|
-
async forceSummarize(messages) {
|
|
491
|
+
async forceSummarize(messages, signal) {
|
|
492
492
|
let result = messages;
|
|
493
493
|
// Tier 0: same waste-removal + micro as the automatic path.
|
|
494
494
|
result = this.persistLargeToolResults(result);
|
|
@@ -505,7 +505,7 @@ export class ContextManager {
|
|
|
505
505
|
let tokens = this.estimateTokensHybrid(result);
|
|
506
506
|
// Unconditional LLM summary (no ratio gate).
|
|
507
507
|
{
|
|
508
|
-
const summarized = await this.trySummaryCompact(result, tokens, "context.force_summary_compact");
|
|
508
|
+
const summarized = await this.trySummaryCompact(result, tokens, "context.force_summary_compact", signal);
|
|
509
509
|
result = summarized.messages;
|
|
510
510
|
tokens = summarized.tokens;
|
|
511
511
|
if (summarized.compacted)
|
|
@@ -15,6 +15,9 @@ const RATIOS = {
|
|
|
15
15
|
mixed: 3.6, // Mixed content (typical for tool results)
|
|
16
16
|
cjk: 1.5, // CJK characters: ~1.5 chars per token
|
|
17
17
|
};
|
|
18
|
+
// Base64 is transport data, not text. A fixed vision estimate prevents large
|
|
19
|
+
// images from spuriously exhausting the text context window.
|
|
20
|
+
const IMAGE_TOKEN_ESTIMATE = 170;
|
|
18
21
|
/**
|
|
19
22
|
* Estimate token count for a string, detecting content type.
|
|
20
23
|
*/
|
|
@@ -80,12 +83,11 @@ function estimateBlockTokens(block) {
|
|
|
80
83
|
if (block.name)
|
|
81
84
|
tokens += estimateStringTokens(block.name);
|
|
82
85
|
if (block.source?.data) {
|
|
83
|
-
tokens +=
|
|
84
|
-
tokens += estimateStringTokens(block.source.data);
|
|
86
|
+
tokens += IMAGE_TOKEN_ESTIMATE;
|
|
85
87
|
}
|
|
86
88
|
const maybeOpenAI = block;
|
|
87
89
|
if (typeof maybeOpenAI.image_url?.url === "string") {
|
|
88
|
-
tokens +=
|
|
90
|
+
tokens += IMAGE_TOKEN_ESTIMATE;
|
|
89
91
|
}
|
|
90
92
|
return tokens;
|
|
91
93
|
}
|
|
@@ -55,6 +55,12 @@ export declare function reconstructContentReplacementState(messages: Message[]):
|
|
|
55
55
|
* transcript path. Layout: <transcriptDir>/tool-results/
|
|
56
56
|
*/
|
|
57
57
|
export declare function resolveToolResultsDir(transcriptPath: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Convert an externally supplied tool_use_id to a bounded internal filename.
|
|
60
|
+
* The original id remains the key in ContentReplacementState/onPersist; it is
|
|
61
|
+
* never used as a path component.
|
|
62
|
+
*/
|
|
63
|
+
export declare function safeToolResultFilename(toolUseId: string): string;
|
|
58
64
|
interface PersistOptions {
|
|
59
65
|
/** Per-result threshold; default DEFAULT_PERSIST_THRESHOLD. */
|
|
60
66
|
perResultThreshold?: number;
|