@use-aistack/cli 0.12.4 → 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 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.12.4" : "0.0.0-dev";
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";
@@ -2408,8 +2408,9 @@ async function hasRecentFile(roots, matches, sinceMs, opts = {}) {
2408
2408
  }
2409
2409
 
2410
2410
  // ../workflow-rules/src/daily.ts
2411
- var WORKFLOW_AGGREGATES_V2 = "workflow-aggregates/v2";
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 days) {
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 = WORKFLOW_AGGREGATES_V2;
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") ingestClaudeTurnDuration(agg, rec, ctx, tsMs);
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) continue;
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) ingestEvent(agg, payload, state, tsMs);
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
- if (asStr(payload.type) !== "token_count") return;
4855
- const info = asObj(payload.info);
4856
- const last = info ? asObj(info.last_token_usage) : null;
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",
@@ -7434,7 +7672,7 @@ function buildWorkflowExtraction(harnessWorkflows, git, utcOffsetMinutes = machi
7434
7672
  ])
7435
7673
  ].sort();
7436
7674
  return {
7437
- aggregateVersion: WORKFLOW_AGGREGATES_V2,
7675
+ aggregateVersion: WORKFLOW_AGGREGATES_V3,
7438
7676
  utcOffsetMinutes,
7439
7677
  days: dates.map((date) => {
7440
7678
  const projects = projectDays.get(date)?.size;
@@ -7690,7 +7928,7 @@ var PHASE_ORDER = ["scout", "build", "verify", "handoff", "unknown"];
7690
7928
  function workflowBlock(workflowDays, utcOffsetMinutes, host) {
7691
7929
  const out = [];
7692
7930
  const folded = foldWorkflowDays(workflowDays, {
7693
- aggregateVersion: WORKFLOW_AGGREGATES_V2,
7931
+ aggregateVersion: WORKFLOW_AGGREGATES_V3,
7694
7932
  utcOffsetMinutes
7695
7933
  });
7696
7934
  const harnesses = folded?.harnesses ?? [];
@@ -7700,7 +7938,7 @@ function workflowBlock(workflowDays, utcOffsetMinutes, host) {
7700
7938
  ...new Set(withPlaybook.map((h) => h.phase?.ruleVersion ?? ""))
7701
7939
  ].filter(Boolean);
7702
7940
  out.push(
7703
- `workflow ${harnesses.length} harness${harnesses.length === 1 ? "" : "es"} \xB7 ${sessions} sessions \xB7 ${WORKFLOW_AGGREGATES_V2}`
7941
+ `workflow ${harnesses.length} harness${harnesses.length === 1 ? "" : "es"} \xB7 ${sessions} sessions \xB7 ${WORKFLOW_AGGREGATES_V3}`
7704
7942
  );
7705
7943
  const first = folded?.dates[0];
7706
7944
  const last = folded?.dates.at(-1);