@use-aistack/cli 0.12.2 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +344 -45
- package/dist/index.js.map +1 -1
- package/package.json +11 -10
- package/LICENSE +0 -21
package/dist/index.js
CHANGED
|
@@ -348,7 +348,7 @@ function apiEquivalentCost(modelKey, t, atMs) {
|
|
|
348
348
|
}
|
|
349
349
|
|
|
350
350
|
// src/version.ts
|
|
351
|
-
var CLI_VERSION = true ? "0.
|
|
351
|
+
var CLI_VERSION = true ? "0.13.0" : "0.0.0-dev";
|
|
352
352
|
|
|
353
353
|
// src/api.ts
|
|
354
354
|
var BASE_URL = process.env.AISTACK_URL || "https://aistack.to";
|
|
@@ -976,11 +976,11 @@ function readText(path7) {
|
|
|
976
976
|
return null;
|
|
977
977
|
}
|
|
978
978
|
}
|
|
979
|
-
function readParsed(path7,
|
|
979
|
+
function readParsed(path7, parse2) {
|
|
980
980
|
const raw = readText(path7);
|
|
981
981
|
if (raw === null) return null;
|
|
982
982
|
try {
|
|
983
|
-
return
|
|
983
|
+
return parse2(raw);
|
|
984
984
|
} catch {
|
|
985
985
|
return null;
|
|
986
986
|
}
|
|
@@ -2408,8 +2408,9 @@ async function hasRecentFile(roots, matches, sinceMs, opts = {}) {
|
|
|
2408
2408
|
}
|
|
2409
2409
|
|
|
2410
2410
|
// ../workflow-rules/src/daily.ts
|
|
2411
|
-
var
|
|
2411
|
+
var WORKFLOW_AGGREGATES_V3 = "workflow-aggregates/v3";
|
|
2412
2412
|
var LOG_BUCKETS_V1 = "log-buckets/v1";
|
|
2413
|
+
var LOG_BUCKETS_V2 = "log-buckets/v2";
|
|
2413
2414
|
var EMPTY_PHASE_TOTALS = Object.freeze({
|
|
2414
2415
|
scout: 0,
|
|
2415
2416
|
build: 0,
|
|
@@ -2463,6 +2464,10 @@ function medianBucket(buckets) {
|
|
|
2463
2464
|
}
|
|
2464
2465
|
return sorted[sorted.length - 1]?.bucket;
|
|
2465
2466
|
}
|
|
2467
|
+
function logBucketV2(value) {
|
|
2468
|
+
if (!(value >= 1)) return 0;
|
|
2469
|
+
return Math.floor(2 * Math.log2(value)) + 1;
|
|
2470
|
+
}
|
|
2466
2471
|
function median(values) {
|
|
2467
2472
|
if (values.length === 0) return void 0;
|
|
2468
2473
|
const sorted = [...values].sort((a, b) => a - b);
|
|
@@ -2505,6 +2510,52 @@ function foldModels(rows) {
|
|
|
2505
2510
|
(row) => ({ ...row })
|
|
2506
2511
|
).sort((a, b) => b.tokens - a.tokens || a.model.localeCompare(b.model));
|
|
2507
2512
|
}
|
|
2513
|
+
function foldCountBuckets(rows, field) {
|
|
2514
|
+
return sumBy(
|
|
2515
|
+
rows,
|
|
2516
|
+
(row) => String(row.bucket),
|
|
2517
|
+
(into, from) => {
|
|
2518
|
+
into[field] += from[field];
|
|
2519
|
+
},
|
|
2520
|
+
(row) => ({ ...row })
|
|
2521
|
+
).sort((a, b) => a.bucket - b.bucket);
|
|
2522
|
+
}
|
|
2523
|
+
function foldContextDays(days) {
|
|
2524
|
+
const versions = [...new Set(days.map((d) => d.bucketRuleVersion))].sort().join(" \xB7 ");
|
|
2525
|
+
const windows = days.map((d) => d.window).filter((w) => w !== void 0);
|
|
2526
|
+
const window = windows[windows.length - 1];
|
|
2527
|
+
return {
|
|
2528
|
+
bucketRuleVersion: versions,
|
|
2529
|
+
calls: {
|
|
2530
|
+
main: foldCountBuckets(
|
|
2531
|
+
days.flatMap((d) => d.calls.main),
|
|
2532
|
+
"calls"
|
|
2533
|
+
),
|
|
2534
|
+
subagents: foldCountBuckets(
|
|
2535
|
+
days.flatMap((d) => d.calls.subagents),
|
|
2536
|
+
"calls"
|
|
2537
|
+
)
|
|
2538
|
+
},
|
|
2539
|
+
firstCalls: {
|
|
2540
|
+
main: foldCountBuckets(
|
|
2541
|
+
days.flatMap((d) => d.firstCalls.main),
|
|
2542
|
+
"sessions"
|
|
2543
|
+
)
|
|
2544
|
+
},
|
|
2545
|
+
firstCallHarnessTokens: days.reduce(
|
|
2546
|
+
(sum, d) => sum + d.firstCallHarnessTokens,
|
|
2547
|
+
0
|
|
2548
|
+
),
|
|
2549
|
+
firstCallInstructionsTokens: days.reduce(
|
|
2550
|
+
(sum, d) => sum + d.firstCallInstructionsTokens,
|
|
2551
|
+
0
|
|
2552
|
+
),
|
|
2553
|
+
firstCallCount: days.reduce((sum, d) => sum + d.firstCallCount, 0),
|
|
2554
|
+
maxContext: Math.max(0, ...days.map((d) => d.maxContext)),
|
|
2555
|
+
compactions: days.reduce((sum, d) => sum + d.compactions, 0),
|
|
2556
|
+
...window === void 0 ? {} : { window }
|
|
2557
|
+
};
|
|
2558
|
+
}
|
|
2508
2559
|
function foldLengths(rows) {
|
|
2509
2560
|
return sumBy(
|
|
2510
2561
|
rows,
|
|
@@ -2641,6 +2692,8 @@ function foldHarnessDays(days) {
|
|
|
2641
2692
|
0
|
|
2642
2693
|
);
|
|
2643
2694
|
}
|
|
2695
|
+
const contexts = days.flatMap((day) => day.context ? [day.context] : []);
|
|
2696
|
+
if (contexts.length > 0) out.context = foldContextDays(contexts);
|
|
2644
2697
|
return out;
|
|
2645
2698
|
}
|
|
2646
2699
|
var MAX_CHANGED_LINES_PER_COMMIT = 4096;
|
|
@@ -2690,8 +2743,9 @@ function foldGitDays(days) {
|
|
|
2690
2743
|
}
|
|
2691
2744
|
function foldWorkflowDays(days, options) {
|
|
2692
2745
|
if (days.length === 0) return void 0;
|
|
2746
|
+
const dated = [...days].sort((a, b) => a.date.localeCompare(b.date));
|
|
2693
2747
|
const byHarness = /* @__PURE__ */ new Map();
|
|
2694
|
-
for (const day of
|
|
2748
|
+
for (const day of dated) {
|
|
2695
2749
|
for (const harness of day.harnesses) {
|
|
2696
2750
|
const held = byHarness.get(harness.harness) ?? [];
|
|
2697
2751
|
held.push(harness);
|
|
@@ -3863,7 +3917,7 @@ function buildSyncBody(built, syncConfig, autoSync, trigger = "manual", measured
|
|
|
3863
3917
|
}
|
|
3864
3918
|
|
|
3865
3919
|
// src/workflow/reducer.ts
|
|
3866
|
-
var WORKFLOW_AGGREGATE_VERSION =
|
|
3920
|
+
var WORKFLOW_AGGREGATE_VERSION = WORKFLOW_AGGREGATES_V3;
|
|
3867
3921
|
function createWorkflowLocalSources() {
|
|
3868
3922
|
return { projectWorkspaces: /* @__PURE__ */ new Set(), activeProjectDays: /* @__PURE__ */ new Map() };
|
|
3869
3923
|
}
|
|
@@ -3879,6 +3933,11 @@ var bump2 = (map, key2, amount = 1) => {
|
|
|
3879
3933
|
map.set(key2, (map.get(key2) ?? 0) + amount);
|
|
3880
3934
|
};
|
|
3881
3935
|
var utcDateOf2 = (ms) => new Date(ms).toISOString().slice(0, 10);
|
|
3936
|
+
function asBuckets(map, field) {
|
|
3937
|
+
return [...map].map(
|
|
3938
|
+
([bucket, count]) => ({ bucket, [field]: count })
|
|
3939
|
+
).sort((a, b) => a.bucket - b.bucket);
|
|
3940
|
+
}
|
|
3882
3941
|
var PHASE_RANK = {
|
|
3883
3942
|
verify: 4,
|
|
3884
3943
|
handoff: 3,
|
|
@@ -3976,13 +4035,25 @@ function dayState() {
|
|
|
3976
4035
|
questions: { asked: 0, turns: 0 },
|
|
3977
4036
|
hasQuestions: false,
|
|
3978
4037
|
webSearches: 0,
|
|
3979
|
-
hasWebSearches: false
|
|
4038
|
+
hasWebSearches: false,
|
|
4039
|
+
context: {
|
|
4040
|
+
calls: { main: /* @__PURE__ */ new Map(), subagents: /* @__PURE__ */ new Map() },
|
|
4041
|
+
firstCalls: /* @__PURE__ */ new Map(),
|
|
4042
|
+
firstCallHarnessTokens: 0,
|
|
4043
|
+
firstCallInstructionsTokens: 0,
|
|
4044
|
+
firstCallCount: 0,
|
|
4045
|
+
maxContext: 0,
|
|
4046
|
+
compactions: 0,
|
|
4047
|
+
window: void 0
|
|
4048
|
+
},
|
|
4049
|
+
hasContext: false
|
|
3980
4050
|
};
|
|
3981
4051
|
}
|
|
3982
4052
|
function createHarnessWorkflowReducer(harness, localSources = createWorkflowLocalSources()) {
|
|
3983
4053
|
const sessions = /* @__PURE__ */ new Map();
|
|
3984
4054
|
const eventCells = /* @__PURE__ */ new Map();
|
|
3985
4055
|
const webSearchesByDate = /* @__PURE__ */ new Map();
|
|
4056
|
+
const compactionsByDate = /* @__PURE__ */ new Map();
|
|
3986
4057
|
const eventDates = /* @__PURE__ */ new Set();
|
|
3987
4058
|
let finished;
|
|
3988
4059
|
const getSession = (key2) => {
|
|
@@ -4023,22 +4094,39 @@ function createHarnessWorkflowReducer(harness, localSources = createWorkflowLoca
|
|
|
4023
4094
|
} else if (observation.type === "response") {
|
|
4024
4095
|
const responseId = observation.responseId ?? `anonymous:${state.nextAnonymousResponse++}`;
|
|
4025
4096
|
const duration = finiteNonnegative(observation.durationSec);
|
|
4097
|
+
const contextTokens = finiteNonnegative(observation.contextTokens);
|
|
4098
|
+
const contextWindow = finiteNonnegative(observation.contextWindow);
|
|
4026
4099
|
const response = {
|
|
4100
|
+
tsMs: observation.tsMs,
|
|
4027
4101
|
...observation.model ? { model: observation.model } : {},
|
|
4028
4102
|
...observation.thinkingTokens !== void 0 ? { thinkingTokens: finiteNonnegative(observation.thinkingTokens) } : {},
|
|
4029
4103
|
...observation.responseTokens !== void 0 ? { responseTokens: finiteNonnegative(observation.responseTokens) } : {},
|
|
4030
4104
|
...observation.routingTokens !== void 0 ? { routingTokens: finiteNonnegative(observation.routingTokens) } : {},
|
|
4031
4105
|
...observation.effort ? { effort: observation.effort } : {},
|
|
4032
|
-
...duration > 0 ? { durationSec: duration } : {}
|
|
4106
|
+
...duration > 0 ? { durationSec: duration } : {},
|
|
4107
|
+
...observation.contextTokens !== void 0 ? { contextTokens } : {},
|
|
4108
|
+
...contextWindow > 0 ? { contextWindow } : {},
|
|
4109
|
+
...observation.firstCall ? {
|
|
4110
|
+
firstCall: {
|
|
4111
|
+
harnessTokens: finiteNonnegative(
|
|
4112
|
+
observation.firstCall.harnessTokens
|
|
4113
|
+
),
|
|
4114
|
+
instructionsTokens: finiteNonnegative(
|
|
4115
|
+
observation.firstCall.instructionsTokens
|
|
4116
|
+
)
|
|
4117
|
+
}
|
|
4118
|
+
} : {}
|
|
4033
4119
|
};
|
|
4034
4120
|
const existing = state.responses.get(responseId);
|
|
4035
4121
|
const magnitude = (value) => value.routingTokens ?? (value.thinkingTokens ?? 0) + (value.responseTokens ?? 0);
|
|
4036
4122
|
if (!existing || magnitude(response) > magnitude(existing)) {
|
|
4037
4123
|
state.responses.set(responseId, response);
|
|
4038
4124
|
}
|
|
4039
|
-
} else {
|
|
4125
|
+
} else if (observation.type === "turn") {
|
|
4040
4126
|
const turnId = observation.turnId ?? `anonymous:${state.nextAnonymousTurn++}`;
|
|
4041
4127
|
state.turns.set(turnId, observation.questionBack);
|
|
4128
|
+
} else {
|
|
4129
|
+
bump2(compactionsByDate, date);
|
|
4042
4130
|
}
|
|
4043
4131
|
},
|
|
4044
4132
|
finish() {
|
|
@@ -4144,6 +4232,33 @@ function createHarnessWorkflowReducer(harness, localSources = createWorkflowLoca
|
|
|
4144
4232
|
Boolean
|
|
4145
4233
|
).length;
|
|
4146
4234
|
}
|
|
4235
|
+
for (const response of responses) {
|
|
4236
|
+
if (response.contextTokens === void 0) continue;
|
|
4237
|
+
day.hasContext = true;
|
|
4238
|
+
const context = day.context;
|
|
4239
|
+
bump2(context.calls[routing], logBucketV2(response.contextTokens));
|
|
4240
|
+
context.maxContext = Math.max(
|
|
4241
|
+
context.maxContext,
|
|
4242
|
+
response.contextTokens
|
|
4243
|
+
);
|
|
4244
|
+
if (response.contextWindow !== void 0 && (context.window === void 0 || response.tsMs >= context.window.tsMs)) {
|
|
4245
|
+
context.window = {
|
|
4246
|
+
tsMs: response.tsMs,
|
|
4247
|
+
window: response.contextWindow
|
|
4248
|
+
};
|
|
4249
|
+
}
|
|
4250
|
+
if (response.firstCall && routing === "main") {
|
|
4251
|
+
bump2(context.firstCalls, logBucketV2(response.contextTokens));
|
|
4252
|
+
context.firstCallHarnessTokens += response.firstCall.harnessTokens;
|
|
4253
|
+
context.firstCallInstructionsTokens += response.firstCall.instructionsTokens;
|
|
4254
|
+
context.firstCallCount++;
|
|
4255
|
+
}
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
for (const [date, compactions] of compactionsByDate) {
|
|
4259
|
+
const day = dayOf(date);
|
|
4260
|
+
day.hasContext = true;
|
|
4261
|
+
day.context.compactions += compactions;
|
|
4147
4262
|
}
|
|
4148
4263
|
for (const date of eventDates) {
|
|
4149
4264
|
const day = dayOf(date);
|
|
@@ -4276,12 +4391,34 @@ function createHarnessWorkflowReducer(harness, localSources = createWorkflowLoca
|
|
|
4276
4391
|
}
|
|
4277
4392
|
} : {},
|
|
4278
4393
|
...day.hasQuestions ? { questions: day.questions } : {},
|
|
4279
|
-
...day.hasWebSearches ? { webSearches: day.webSearches } : {}
|
|
4394
|
+
...day.hasWebSearches ? { webSearches: day.webSearches } : {},
|
|
4395
|
+
...day.hasContext ? {
|
|
4396
|
+
context: {
|
|
4397
|
+
bucketRuleVersion: LOG_BUCKETS_V2,
|
|
4398
|
+
calls: {
|
|
4399
|
+
main: asBuckets(day.context.calls.main, "calls"),
|
|
4400
|
+
subagents: asBuckets(
|
|
4401
|
+
day.context.calls.subagents,
|
|
4402
|
+
"calls"
|
|
4403
|
+
)
|
|
4404
|
+
},
|
|
4405
|
+
firstCalls: {
|
|
4406
|
+
main: asBuckets(day.context.firstCalls, "sessions")
|
|
4407
|
+
},
|
|
4408
|
+
firstCallHarnessTokens: day.context.firstCallHarnessTokens,
|
|
4409
|
+
firstCallInstructionsTokens: day.context.firstCallInstructionsTokens,
|
|
4410
|
+
firstCallCount: day.context.firstCallCount,
|
|
4411
|
+
maxContext: day.context.maxContext,
|
|
4412
|
+
compactions: day.context.compactions,
|
|
4413
|
+
...day.context.window ? { window: day.context.window.window } : {}
|
|
4414
|
+
}
|
|
4415
|
+
} : {}
|
|
4280
4416
|
}))
|
|
4281
4417
|
};
|
|
4282
4418
|
sessions.clear();
|
|
4283
4419
|
eventCells.clear();
|
|
4284
4420
|
webSearchesByDate.clear();
|
|
4421
|
+
compactionsByDate.clear();
|
|
4285
4422
|
eventDates.clear();
|
|
4286
4423
|
return finished;
|
|
4287
4424
|
}
|
|
@@ -4295,9 +4432,30 @@ function createAggregate2() {
|
|
|
4295
4432
|
workflow: createHarnessWorkflowReducer("claude-code", workflowLocal),
|
|
4296
4433
|
workflowLocal,
|
|
4297
4434
|
workflowSeenCalls: /* @__PURE__ */ new Set(),
|
|
4298
|
-
workflowSeenTurns: /* @__PURE__ */ new Set()
|
|
4435
|
+
workflowSeenTurns: /* @__PURE__ */ new Set(),
|
|
4436
|
+
contextFirstCall: /* @__PURE__ */ new Map()
|
|
4299
4437
|
});
|
|
4300
4438
|
}
|
|
4439
|
+
function workflowSessionKey(rec) {
|
|
4440
|
+
const baseSession = asStr(rec.sessionId);
|
|
4441
|
+
if (!baseSession) return null;
|
|
4442
|
+
if (rec.isSidechain !== true) return baseSession;
|
|
4443
|
+
return `${baseSession}:agent:${asStr(rec.agentId) ?? "unknown"}`;
|
|
4444
|
+
}
|
|
4445
|
+
function isApiCall(rec) {
|
|
4446
|
+
if (asStr(rec.type) !== "assistant") return false;
|
|
4447
|
+
const msg = asObj(rec.message);
|
|
4448
|
+
if (!msg || !asObj(msg.usage)) return false;
|
|
4449
|
+
return !(asName(msg.model) ?? "").startsWith("<");
|
|
4450
|
+
}
|
|
4451
|
+
function noteRecordBeforeWindow(agg, raw) {
|
|
4452
|
+
const rec = asObj(raw);
|
|
4453
|
+
if (!rec || !isApiCall(rec)) return;
|
|
4454
|
+
const session = workflowSessionKey(rec);
|
|
4455
|
+
if (session && !agg.contextFirstCall.has(session)) {
|
|
4456
|
+
agg.contextFirstCall.set(session, null);
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4301
4459
|
function projectWorkspaceDirectory(raw) {
|
|
4302
4460
|
const rec = asObj(raw);
|
|
4303
4461
|
return rec ? asStr(rec.cwd) : null;
|
|
@@ -4330,7 +4488,25 @@ function ingestRecord(agg, raw, ctx) {
|
|
|
4330
4488
|
ingestClaudeWorkflow(agg, rec, ctx, tsMs);
|
|
4331
4489
|
ingestAssistant(agg, rec, tsMs);
|
|
4332
4490
|
} else if (type === "user") ingestUser(agg, rec);
|
|
4333
|
-
else if (type === "system")
|
|
4491
|
+
else if (type === "system") {
|
|
4492
|
+
ingestClaudeTurnDuration(agg, rec, ctx, tsMs);
|
|
4493
|
+
ingestClaudeCompaction(agg, rec, ctx, tsMs);
|
|
4494
|
+
}
|
|
4495
|
+
}
|
|
4496
|
+
function ingestClaudeCompaction(agg, rec, ctx, tsMs) {
|
|
4497
|
+
if (tsMs === null || asStr(rec.subtype) !== "compact_boundary") return;
|
|
4498
|
+
const session = workflowSessionKey(rec);
|
|
4499
|
+
if (!session) return;
|
|
4500
|
+
agg.workflow.ingest({
|
|
4501
|
+
type: "compaction",
|
|
4502
|
+
session,
|
|
4503
|
+
projectWorkspace: projectWorkspaceDirectory(rec) ?? ctx.projectDir,
|
|
4504
|
+
tsMs,
|
|
4505
|
+
...rec.isSidechain === true ? { sidechain: true } : {}
|
|
4506
|
+
});
|
|
4507
|
+
}
|
|
4508
|
+
function contextOf(counts) {
|
|
4509
|
+
return counts.input + counts.cacheWrite5m + counts.cacheWrite1h + counts.cacheWriteUnsplit + counts.cacheRead;
|
|
4334
4510
|
}
|
|
4335
4511
|
function ingestClaudeTurnDuration(agg, rec, ctx, tsMs) {
|
|
4336
4512
|
if (tsMs === null || asStr(rec.subtype) !== "turn_duration" || asNum(rec.durationMs) <= 0) {
|
|
@@ -4362,6 +4538,24 @@ function ingestClaudeWorkflow(agg, rec, ctx, tsMs) {
|
|
|
4362
4538
|
const messageId = asStr(msg.id);
|
|
4363
4539
|
const newTurn = messageId ? !agg.workflowSeenTurns.has(messageId) : true;
|
|
4364
4540
|
if (messageId) agg.workflowSeenTurns.add(messageId);
|
|
4541
|
+
let context = null;
|
|
4542
|
+
if (counts && isApiCall(rec)) {
|
|
4543
|
+
const held = agg.contextFirstCall.get(session);
|
|
4544
|
+
let first = false;
|
|
4545
|
+
if (held === void 0) {
|
|
4546
|
+
agg.contextFirstCall.set(session, messageId);
|
|
4547
|
+
first = true;
|
|
4548
|
+
} else if (held !== null && held === messageId) first = true;
|
|
4549
|
+
context = {
|
|
4550
|
+
contextTokens: contextOf(counts),
|
|
4551
|
+
...first ? {
|
|
4552
|
+
firstCall: {
|
|
4553
|
+
harnessTokens: counts.cacheRead,
|
|
4554
|
+
instructionsTokens: contextOf(counts) - counts.cacheRead
|
|
4555
|
+
}
|
|
4556
|
+
} : {}
|
|
4557
|
+
};
|
|
4558
|
+
}
|
|
4365
4559
|
agg.workflow.ingest({
|
|
4366
4560
|
type: "response",
|
|
4367
4561
|
session,
|
|
@@ -4373,7 +4567,8 @@ function ingestClaudeWorkflow(agg, rec, ctx, tsMs) {
|
|
|
4373
4567
|
...asName(msg.model) ? { model: asName(msg.model) } : {},
|
|
4374
4568
|
...counts ? { responseTokens: counts.output } : {},
|
|
4375
4569
|
...counts ? { routingTokens: countsTotal(counts) } : {},
|
|
4376
|
-
...asStr(rec.effort) ?? asStr(msg.effort) ? { effort: asStr(rec.effort) ?? asStr(msg.effort) } : {}
|
|
4570
|
+
...asStr(rec.effort) ?? asStr(msg.effort) ? { effort: asStr(rec.effort) ?? asStr(msg.effort) } : {},
|
|
4571
|
+
...context ?? {}
|
|
4377
4572
|
});
|
|
4378
4573
|
const tools = [];
|
|
4379
4574
|
for (const rawBlock of asArr(msg.content)) {
|
|
@@ -4751,7 +4946,10 @@ async function ingestFile(agg, file, projectDir, projectWorkspaces, sinceMs) {
|
|
|
4751
4946
|
}
|
|
4752
4947
|
if (sinceMs !== void 0) {
|
|
4753
4948
|
const ts = rec && typeof rec === "object" && "timestamp" in rec && typeof rec.timestamp === "string" ? Date.parse(rec.timestamp) : Number.NaN;
|
|
4754
|
-
if (Number.isNaN(ts) || ts < sinceMs)
|
|
4949
|
+
if (Number.isNaN(ts) || ts < sinceMs) {
|
|
4950
|
+
noteRecordBeforeWindow(agg, rec);
|
|
4951
|
+
continue;
|
|
4952
|
+
}
|
|
4755
4953
|
}
|
|
4756
4954
|
ingestRecord(agg, rec, { projectDir: projectWorkspace });
|
|
4757
4955
|
}
|
|
@@ -4803,7 +5001,33 @@ function createFileState() {
|
|
|
4803
5001
|
responseIndex: 0,
|
|
4804
5002
|
currentQuestionBack: false,
|
|
4805
5003
|
counted: false,
|
|
4806
|
-
metaTsMs: null
|
|
5004
|
+
metaTsMs: null,
|
|
5005
|
+
forked: false,
|
|
5006
|
+
sawResponse: false
|
|
5007
|
+
};
|
|
5008
|
+
}
|
|
5009
|
+
function genuineDelta(payload, state, tsMs) {
|
|
5010
|
+
if (asStr(payload.type) !== "token_count") return null;
|
|
5011
|
+
const info = asObj(payload.info);
|
|
5012
|
+
const last = info ? asObj(info.last_token_usage) : null;
|
|
5013
|
+
if (!last) return null;
|
|
5014
|
+
const inputTotal = asNum(last.input_tokens);
|
|
5015
|
+
const cached = Math.min(asNum(last.cached_input_tokens), inputTotal);
|
|
5016
|
+
const counts = {
|
|
5017
|
+
input: inputTotal - cached,
|
|
5018
|
+
output: asNum(last.output_tokens),
|
|
5019
|
+
cacheWrite5m: 0,
|
|
5020
|
+
cacheWrite1h: 0,
|
|
5021
|
+
cacheWriteUnsplit: 0,
|
|
5022
|
+
cacheRead: cached
|
|
5023
|
+
};
|
|
5024
|
+
if (countsTotal(counts) === 0) return null;
|
|
5025
|
+
if (tsMs !== null && state.metaTsMs !== null && tsMs - state.metaTsMs < FORK_REPLAY_WINDOW_MS)
|
|
5026
|
+
return null;
|
|
5027
|
+
return {
|
|
5028
|
+
counts,
|
|
5029
|
+
last,
|
|
5030
|
+
contextWindow: info ? asNum(info.model_context_window) : 0
|
|
4807
5031
|
};
|
|
4808
5032
|
}
|
|
4809
5033
|
var FORK_REPLAY_WINDOW_MS = 500;
|
|
@@ -4825,11 +5049,17 @@ function ingestLine(agg, raw, state, sinceMs) {
|
|
|
4825
5049
|
state.cliVersion = asStr(payload.cli_version) ?? state.cliVersion;
|
|
4826
5050
|
state.cwd = asStr(payload.cwd) ?? state.cwd;
|
|
4827
5051
|
state.metaTsMs = tsMs ?? state.metaTsMs;
|
|
5052
|
+
state.forked = payload.forked_from_id !== void 0 && payload.forked_from_id !== null;
|
|
4828
5053
|
} else if (type === "turn_context" && payload) {
|
|
4829
5054
|
const model = asName(payload.model);
|
|
4830
5055
|
if (model) state.modelKey = normalizeModel(model);
|
|
4831
5056
|
state.effort = asStr(payload.effort) ?? state.effort;
|
|
4832
5057
|
}
|
|
5058
|
+
let firstCall = false;
|
|
5059
|
+
if (type === "event_msg" && payload && genuineDelta(payload, state, tsMs)) {
|
|
5060
|
+
firstCall = !state.sawResponse;
|
|
5061
|
+
state.sawResponse = true;
|
|
5062
|
+
}
|
|
4833
5063
|
if (!inWindow) return;
|
|
4834
5064
|
if (tsMs !== null && timestamp) {
|
|
4835
5065
|
agg.activeDays.add(timestamp.slice(0, 10));
|
|
@@ -4838,9 +5068,18 @@ function ingestLine(agg, raw, state, sinceMs) {
|
|
|
4838
5068
|
}
|
|
4839
5069
|
noteActivity(agg, state, tsMs);
|
|
4840
5070
|
noteProjectDay(agg, state.cwd ?? "(unknown)", tsMs);
|
|
4841
|
-
if (type === "event_msg" && payload)
|
|
5071
|
+
if (type === "event_msg" && payload)
|
|
5072
|
+
ingestEvent(agg, payload, state, tsMs, firstCall);
|
|
4842
5073
|
else if (type === "response_item" && payload)
|
|
4843
5074
|
ingestItem(agg, payload, state, tsMs);
|
|
5075
|
+
else if (type === "compacted" && tsMs !== null && state.sessionId) {
|
|
5076
|
+
agg.workflow.ingest({
|
|
5077
|
+
type: "compaction",
|
|
5078
|
+
session: state.sessionId,
|
|
5079
|
+
projectWorkspace: state.cwd ?? void 0,
|
|
5080
|
+
tsMs
|
|
5081
|
+
});
|
|
5082
|
+
}
|
|
4844
5083
|
}
|
|
4845
5084
|
function noteActivity(agg, state, tsMs) {
|
|
4846
5085
|
if (state.sessionId) noteSessionStart(agg, state.sessionId, tsMs);
|
|
@@ -4850,25 +5089,11 @@ function noteActivity(agg, state, tsMs) {
|
|
|
4850
5089
|
if (state.cliVersion) agg.ccVersions.add(cleanName(state.cliVersion));
|
|
4851
5090
|
agg.projectDirs.add(state.cwd ?? "(unknown)");
|
|
4852
5091
|
}
|
|
4853
|
-
function ingestEvent(agg, payload, state, tsMs) {
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
const
|
|
4857
|
-
if (!last) return;
|
|
4858
|
-
const inputTotal = asNum(last.input_tokens);
|
|
4859
|
-
const cached = Math.min(asNum(last.cached_input_tokens), inputTotal);
|
|
4860
|
-
const counts = {
|
|
4861
|
-
input: inputTotal - cached,
|
|
4862
|
-
output: asNum(last.output_tokens),
|
|
4863
|
-
cacheWrite5m: 0,
|
|
4864
|
-
cacheWrite1h: 0,
|
|
4865
|
-
cacheWriteUnsplit: 0,
|
|
4866
|
-
cacheRead: cached
|
|
4867
|
-
};
|
|
5092
|
+
function ingestEvent(agg, payload, state, tsMs, firstCall) {
|
|
5093
|
+
const delta = genuineDelta(payload, state, tsMs);
|
|
5094
|
+
if (!delta) return;
|
|
5095
|
+
const { counts, last, contextWindow } = delta;
|
|
4868
5096
|
const total = countsTotal(counts);
|
|
4869
|
-
if (total === 0) return;
|
|
4870
|
-
if (tsMs !== null && state.metaTsMs !== null && tsMs - state.metaTsMs < FORK_REPLAY_WINDOW_MS)
|
|
4871
|
-
return;
|
|
4872
5097
|
if (state.modelKey === null) return;
|
|
4873
5098
|
if (tsMs === null) agg.untimestampedResponses++;
|
|
4874
5099
|
agg.distinctResponses++;
|
|
@@ -4894,7 +5119,20 @@ function ingestEvent(agg, payload, state, tsMs) {
|
|
|
4894
5119
|
responseTokens: counts.output,
|
|
4895
5120
|
routingTokens: total,
|
|
4896
5121
|
thinkingTokens: asNum(last.reasoning_output_tokens),
|
|
4897
|
-
...state.effort ? { effort: state.effort } : {}
|
|
5122
|
+
...state.effort ? { effort: state.effort } : {},
|
|
5123
|
+
// The context at this call is `input_tokens` (cached is a subset).
|
|
5124
|
+
// On the first call the cached part is the harness (base
|
|
5125
|
+
// instructions and tool specs, cached by an earlier session) and
|
|
5126
|
+
// the fresh part is the instructions: AGENTS.md, environment,
|
|
5127
|
+
// skills and the first prompt. Same method as Claude Code (#358).
|
|
5128
|
+
contextTokens: counts.input + counts.cacheRead,
|
|
5129
|
+
...contextWindow > 0 ? { contextWindow } : {},
|
|
5130
|
+
...firstCall && !state.forked ? {
|
|
5131
|
+
firstCall: {
|
|
5132
|
+
harnessTokens: counts.cacheRead,
|
|
5133
|
+
instructionsTokens: counts.input
|
|
5134
|
+
}
|
|
5135
|
+
} : {}
|
|
4898
5136
|
});
|
|
4899
5137
|
agg.workflow.ingest({
|
|
4900
5138
|
type: "turn",
|
|
@@ -6476,6 +6714,7 @@ import { createHash } from "node:crypto";
|
|
|
6476
6714
|
import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6477
6715
|
import { homedir as homedir11 } from "node:os";
|
|
6478
6716
|
import { dirname as dirname5, join as join8 } from "node:path";
|
|
6717
|
+
import { parse } from "smol-toml";
|
|
6479
6718
|
function codexHome2() {
|
|
6480
6719
|
return process.env.CODEX_HOME || join8(homedir11(), ".codex");
|
|
6481
6720
|
}
|
|
@@ -6558,15 +6797,59 @@ function codexAutoSyncHookInstalled(file = codexHooksFile()) {
|
|
|
6558
6797
|
if (!Array.isArray(sessionStart)) return false;
|
|
6559
6798
|
return sessionStart.some((m) => (m.hooks ?? []).some((h) => isOurs(h)));
|
|
6560
6799
|
}
|
|
6561
|
-
function codexHookTrusted(configFile = codexConfigFile()) {
|
|
6562
|
-
let text;
|
|
6800
|
+
function codexHookTrusted(configFile = codexConfigFile(), hooksFile = codexHooksFile()) {
|
|
6563
6801
|
try {
|
|
6564
|
-
|
|
6802
|
+
const config = parse(readFileSync9(configFile, "utf-8"));
|
|
6803
|
+
const read = readHooksJson(hooksFile);
|
|
6804
|
+
if ("error" in read) return null;
|
|
6805
|
+
const sessionStart = read.settings.hooks?.SessionStart;
|
|
6806
|
+
if (!Array.isArray(sessionStart)) return false;
|
|
6807
|
+
const matches = [];
|
|
6808
|
+
for (const [groupIndex, group] of sessionStart.entries()) {
|
|
6809
|
+
for (const [handlerIndex, handler] of (group.hooks ?? []).entries()) {
|
|
6810
|
+
if (!isOurs(handler) || typeof handler.command !== "string") continue;
|
|
6811
|
+
const normalizedHandler = {
|
|
6812
|
+
type: "command",
|
|
6813
|
+
command: handler.command,
|
|
6814
|
+
timeout: typeof handler.timeout === "number" ? Math.max(1, handler.timeout) : 600,
|
|
6815
|
+
async: handler.async === true
|
|
6816
|
+
};
|
|
6817
|
+
if (typeof handler.statusMessage === "string") {
|
|
6818
|
+
normalizedHandler.statusMessage = handler.statusMessage;
|
|
6819
|
+
}
|
|
6820
|
+
if (typeof handler.additionalContextLimit === "number" && handler.additionalContextLimit !== 2500) {
|
|
6821
|
+
normalizedHandler.additionalContextLimit = handler.additionalContextLimit;
|
|
6822
|
+
}
|
|
6823
|
+
const identity = {
|
|
6824
|
+
event_name: "session_start",
|
|
6825
|
+
hooks: [normalizedHandler]
|
|
6826
|
+
};
|
|
6827
|
+
if (typeof group.matcher === "string") identity.matcher = group.matcher;
|
|
6828
|
+
const currentHash = `sha256:${createHash("sha256").update(JSON.stringify(canonicalJson2(identity))).digest("hex")}`;
|
|
6829
|
+
const key2 = `${hooksFile}:session_start:${groupIndex}:${handlerIndex}`;
|
|
6830
|
+
matches.push(config.hooks?.state?.[key2]?.trusted_hash === currentHash);
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
return matches.length > 0 && matches.every(Boolean);
|
|
6565
6834
|
} catch {
|
|
6566
6835
|
return null;
|
|
6567
6836
|
}
|
|
6568
|
-
|
|
6569
|
-
|
|
6837
|
+
}
|
|
6838
|
+
function canonicalJson2(value) {
|
|
6839
|
+
if (Array.isArray(value)) return value.map(canonicalJson2);
|
|
6840
|
+
if (value && typeof value === "object") {
|
|
6841
|
+
const sorted = {};
|
|
6842
|
+
for (const [key2, child] of Object.entries(value).sort(
|
|
6843
|
+
([a], [b]) => a.localeCompare(b)
|
|
6844
|
+
)) {
|
|
6845
|
+
sorted[key2] = canonicalJson2(child);
|
|
6846
|
+
}
|
|
6847
|
+
return sorted;
|
|
6848
|
+
}
|
|
6849
|
+
if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
|
|
6850
|
+
return value;
|
|
6851
|
+
}
|
|
6852
|
+
throw new TypeError("hook identity is not JSON-serializable");
|
|
6570
6853
|
}
|
|
6571
6854
|
|
|
6572
6855
|
// src/autosync/optin.ts
|
|
@@ -6679,7 +6962,9 @@ async function enableAutoSync(frequencyHours = DEFAULT_FREQUENCY_HOURS, deps = {
|
|
|
6679
6962
|
if (names.has(CODEX_HARNESS_NAME)) {
|
|
6680
6963
|
const codexResult = (deps.installCodexHook ?? installCodexAutoSyncHook)();
|
|
6681
6964
|
if (!codexResult.ok) return codexResult;
|
|
6682
|
-
|
|
6965
|
+
if ((deps.codexHookTrustedImpl ?? codexHookTrusted)() !== true) {
|
|
6966
|
+
trustLine = codexResult.message;
|
|
6967
|
+
}
|
|
6683
6968
|
}
|
|
6684
6969
|
saveSettings(
|
|
6685
6970
|
{
|
|
@@ -7387,7 +7672,7 @@ function buildWorkflowExtraction(harnessWorkflows, git, utcOffsetMinutes = machi
|
|
|
7387
7672
|
])
|
|
7388
7673
|
].sort();
|
|
7389
7674
|
return {
|
|
7390
|
-
aggregateVersion:
|
|
7675
|
+
aggregateVersion: WORKFLOW_AGGREGATES_V3,
|
|
7391
7676
|
utcOffsetMinutes,
|
|
7392
7677
|
days: dates.map((date) => {
|
|
7393
7678
|
const projects = projectDays.get(date)?.size;
|
|
@@ -7430,6 +7715,15 @@ function totalUSD(payload) {
|
|
|
7430
7715
|
}
|
|
7431
7716
|
return any ? sum : null;
|
|
7432
7717
|
}
|
|
7718
|
+
function tokenBreakdown(payload) {
|
|
7719
|
+
let fresh = 0;
|
|
7720
|
+
let cached = 0;
|
|
7721
|
+
for (const model of payload.models) {
|
|
7722
|
+
fresh += model.tokens.input + model.tokens.output + model.tokens.cacheWrite;
|
|
7723
|
+
cached += model.tokens.cacheRead;
|
|
7724
|
+
}
|
|
7725
|
+
return fresh + cached === payload.activity.totalTokens ? { fresh, cached } : null;
|
|
7726
|
+
}
|
|
7433
7727
|
function withheldCount(payload) {
|
|
7434
7728
|
const w = payload.inventory.withheld;
|
|
7435
7729
|
return w.builtinTools + w.mcpServers + w.skills + w.subagents + w.slashCommands;
|
|
@@ -7539,10 +7833,15 @@ function payloadBlock(payload, width, ownWindow, stats) {
|
|
|
7539
7833
|
const label = `${harnessLabel2(payload.harness.name)}${payload.harness.version ? ` ${payload.harness.version}` : ""}`;
|
|
7540
7834
|
const days = payload.activity.activeDayDates.length;
|
|
7541
7835
|
const usd = totalUSD(payload);
|
|
7836
|
+
const breakdown = tokenBreakdown(payload);
|
|
7542
7837
|
const totals = [
|
|
7543
7838
|
`${payload.activity.sessions} session${payload.activity.sessions === 1 ? "" : "s"}`,
|
|
7544
7839
|
`${days} active day${days === 1 ? "" : "s"}`,
|
|
7545
|
-
`${fmtTokens(payload.activity.totalTokens)} tokens
|
|
7840
|
+
`${fmtTokens(payload.activity.totalTokens)} tokens processed`,
|
|
7841
|
+
...breakdown ? [
|
|
7842
|
+
`${fmtTokens(breakdown.fresh)} fresh`,
|
|
7843
|
+
`${fmtTokens(breakdown.cached)} cached`
|
|
7844
|
+
] : []
|
|
7546
7845
|
];
|
|
7547
7846
|
out.push(`${label.toUpperCase()} ${payload.activity.sessions}`);
|
|
7548
7847
|
if (payload.activity.totalTokens === 0) {
|
|
@@ -7629,7 +7928,7 @@ var PHASE_ORDER = ["scout", "build", "verify", "handoff", "unknown"];
|
|
|
7629
7928
|
function workflowBlock(workflowDays, utcOffsetMinutes, host) {
|
|
7630
7929
|
const out = [];
|
|
7631
7930
|
const folded = foldWorkflowDays(workflowDays, {
|
|
7632
|
-
aggregateVersion:
|
|
7931
|
+
aggregateVersion: WORKFLOW_AGGREGATES_V3,
|
|
7633
7932
|
utcOffsetMinutes
|
|
7634
7933
|
});
|
|
7635
7934
|
const harnesses = folded?.harnesses ?? [];
|
|
@@ -7639,7 +7938,7 @@ function workflowBlock(workflowDays, utcOffsetMinutes, host) {
|
|
|
7639
7938
|
...new Set(withPlaybook.map((h) => h.phase?.ruleVersion ?? ""))
|
|
7640
7939
|
].filter(Boolean);
|
|
7641
7940
|
out.push(
|
|
7642
|
-
`workflow ${harnesses.length} harness${harnesses.length === 1 ? "" : "es"} \xB7 ${sessions} sessions \xB7 ${
|
|
7941
|
+
`workflow ${harnesses.length} harness${harnesses.length === 1 ? "" : "es"} \xB7 ${sessions} sessions \xB7 ${WORKFLOW_AGGREGATES_V3}`
|
|
7643
7942
|
);
|
|
7644
7943
|
const first = folded?.dates[0];
|
|
7645
7944
|
const last = folded?.dates.at(-1);
|