@prismer/runtime 2.2.63 → 2.2.64

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/cli.cjs CHANGED
@@ -2101,16 +2101,20 @@ function thinkingText(message) {
2101
2101
  }
2102
2102
  function mapUsage(usage) {
2103
2103
  if (!usage) return void 0;
2104
+ const measured = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
2104
2105
  return {
2105
- inputTokens: usage.input,
2106
- cachedInputTokens: usage.cacheRead,
2107
- outputTokens: usage.output,
2108
- totalCostUsd: usage.cost.total,
2109
- contextWindowUsedTokens: usage.totalTokens
2106
+ inputTokens: measured(usage.input),
2107
+ cachedInputTokens: measured(usage.cacheRead),
2108
+ cacheWriteTokens: measured(usage.cacheWrite),
2109
+ outputTokens: measured(usage.output),
2110
+ totalCostUsd: measured(usage.cost?.total),
2111
+ contextWindowUsedTokens: measured(usage.totalTokens)
2110
2112
  };
2111
2113
  }
2112
2114
  function extractAssistantUsage(message) {
2113
- return message.role === "assistant" ? mapUsage(message.usage) : void 0;
2115
+ if (message.role !== "assistant") return void 0;
2116
+ if ((message.stopReason === "error" || message.stopReason === "aborted") && ![message.usage?.input, message.usage?.output, message.usage?.cacheRead, message.usage?.cacheWrite].some((value) => typeof value === "number" && value > 0)) return void 0;
2117
+ return mapUsage(message.usage);
2114
2118
  }
2115
2119
  function modelDefinition(model) {
2116
2120
  return {
@@ -2686,7 +2690,15 @@ var init_agent = __esm({
2686
2690
  };
2687
2691
  })
2688
2692
  },
2689
- streamFn: options.models.streamSimple.bind(options.models),
2693
+ streamFn: (...args) => {
2694
+ const run = this.activeRun;
2695
+ if (run) {
2696
+ run.callCount = (run.callCount ?? 0) + 1;
2697
+ run.modelCall = { sequence: run.callCount, startedAt: Date.now(), clock: performance.now(), firstTextMs: null };
2698
+ this.emitObservation({ type: "model_request_started", provider: this.provider, startedAt: run.modelCall.startedAt, turnId: run.turnId });
2699
+ }
2700
+ return options.models.streamSimple(...args);
2701
+ },
2690
2702
  sessionId: this.id,
2691
2703
  convertToLlm: (messages) => messages.filter(isProviderMessage),
2692
2704
  toolExecution: "parallel",
@@ -2698,7 +2710,7 @@ var init_agent = __esm({
2698
2710
  // 「模型请求了一个不存在的工具」不会产生 tool_started——事件面只记真实执行。
2699
2711
  beforeToolCall: async (context, signal) => {
2700
2712
  if (sink) {
2701
- this.toolStartedAt.set(context.toolCall.id, Date.now());
2713
+ this.toolStartedAt.set(context.toolCall.id, performance.now());
2702
2714
  emitToolEvent(sink, {
2703
2715
  kind: "tool_started",
2704
2716
  name: context.toolCall.name,
@@ -2723,7 +2735,7 @@ var init_agent = __esm({
2723
2735
  name,
2724
2736
  isError: true,
2725
2737
  resultSummary: reason,
2726
- ...startedAt !== void 0 ? { durationMs: Date.now() - startedAt } : {}
2738
+ ...startedAt !== void 0 ? { durationMs: performance.now() - startedAt } : {}
2727
2739
  });
2728
2740
  return { block: true, reason };
2729
2741
  }
@@ -2738,7 +2750,7 @@ var init_agent = __esm({
2738
2750
  name: context.toolCall.name,
2739
2751
  resultSummary: summarizeToolResult(context.result, secrets),
2740
2752
  isError: context.isError,
2741
- ...startedAt !== void 0 ? { durationMs: Date.now() - startedAt } : {}
2753
+ ...startedAt !== void 0 ? { durationMs: performance.now() - startedAt } : {}
2742
2754
  });
2743
2755
  }
2744
2756
  return void 0;
@@ -2854,6 +2866,14 @@ var init_agent = __esm({
2854
2866
  emit(event) {
2855
2867
  for (const subscriber of this.subscribers) subscriber(event);
2856
2868
  }
2869
+ emitObservation(event) {
2870
+ for (const subscriber of this.subscribers) {
2871
+ try {
2872
+ void Promise.resolve(subscriber(event)).catch(() => void 0);
2873
+ } catch {
2874
+ }
2875
+ }
2876
+ }
2857
2877
  recordTimeline(item) {
2858
2878
  this.activeRun?.timeline.push(item);
2859
2879
  this.emit({
@@ -2873,6 +2893,9 @@ var init_agent = __esm({
2873
2893
  case "message_update": {
2874
2894
  const sub = event.assistantMessageEvent;
2875
2895
  if (sub.type === "text_delta" || sub.type === "thinking_delta") {
2896
+ if (sub.type === "text_delta" && sub.delta && run?.modelCall && run.modelCall.firstTextMs === null) {
2897
+ run.modelCall.firstTextMs = performance.now() - run.modelCall.clock;
2898
+ }
2876
2899
  this.emit({
2877
2900
  type: "text_delta",
2878
2901
  provider: this.provider,
@@ -2882,7 +2905,7 @@ var init_agent = __esm({
2882
2905
  });
2883
2906
  break;
2884
2907
  }
2885
- this.handleMessageEvent(event.message);
2908
+ this.handleMessageEvent(event.message, false);
2886
2909
  break;
2887
2910
  }
2888
2911
  case "message_end":
@@ -2920,7 +2943,7 @@ var init_agent = __esm({
2920
2943
  break;
2921
2944
  }
2922
2945
  }
2923
- handleMessageEvent(message) {
2946
+ handleMessageEvent(message, settled = true) {
2924
2947
  const run = this.activeRun;
2925
2948
  if (!run) return;
2926
2949
  if (message.role !== "assistant") return;
@@ -2933,8 +2956,34 @@ var init_agent = __esm({
2933
2956
  run.finalText = text2;
2934
2957
  this.recordTimeline({ type: "assistant_message", text: text2 });
2935
2958
  }
2936
- const usage = extractAssistantUsage(message);
2937
- if (usage) run.usage = usage;
2959
+ const usage = settled ? extractAssistantUsage(message) : void 0;
2960
+ if (settled && !usage) run.usage = {};
2961
+ if (usage) {
2962
+ const previous = run.usage;
2963
+ run.usage = { ...usage };
2964
+ for (const field2 of ["inputTokens", "outputTokens", "cachedInputTokens", "cacheWriteTokens", "totalCostUsd"]) {
2965
+ if (previous?.[field2] !== void 0 && usage[field2] !== void 0) {
2966
+ run.usage[field2] = previous[field2] + usage[field2];
2967
+ } else if (previous) {
2968
+ delete run.usage[field2];
2969
+ }
2970
+ }
2971
+ }
2972
+ if (settled && run.modelCall) {
2973
+ const call = run.modelCall;
2974
+ delete run.modelCall;
2975
+ this.emitObservation({ type: "model_call_observed", provider: this.provider, turnId: run.turnId, call: {
2976
+ sequence: call.sequence,
2977
+ source: "pi-engine",
2978
+ provider: message.provider,
2979
+ model: message.responseModel ?? message.model,
2980
+ startedAt: call.startedAt,
2981
+ durationMs: performance.now() - call.clock,
2982
+ firstTextMs: call.firstTextMs,
2983
+ status: message.stopReason === "error" ? "failed" : message.stopReason === "aborted" ? "canceled" : "completed",
2984
+ usage: usage ?? null
2985
+ } });
2986
+ }
2938
2987
  run.servedModel = message.responseModel ?? message.model;
2939
2988
  run.servedProvider = message.provider;
2940
2989
  }
@@ -16003,7 +16052,7 @@ var require_package = __commonJS({
16003
16052
  "package.json"(exports2, module2) {
16004
16053
  module2.exports = {
16005
16054
  name: "@prismer/runtime",
16006
- version: "2.2.63",
16055
+ version: "2.2.64",
16007
16056
  description: "Prismer Cloud daemon runtime \u2014 TS-only adapter host for hosted IM agents",
16008
16057
  type: "module",
16009
16058
  main: "dist/index.js",
@@ -78652,6 +78701,21 @@ function classifyProviderError(message) {
78652
78701
  return /ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|fetch failed|socket hang up|network/i.test(message) ? "provider_unreachable" : "internal";
78653
78702
  }
78654
78703
  async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
78704
+ const started = performance.now();
78705
+ const observation = {
78706
+ version: 1,
78707
+ source: "runtime",
78708
+ sessionInitMs: null,
78709
+ totalMs: 0,
78710
+ modelCalls: [],
78711
+ droppedModelCalls: 0
78712
+ };
78713
+ const result = await runTurnExecution(envelope2, egress, deps, onToolEvent, observation);
78714
+ observation.totalMs = performance.now() - started;
78715
+ result.observation = observation;
78716
+ return result;
78717
+ }
78718
+ async function runTurnExecution(envelope2, egress, deps, onToolEvent, observation) {
78655
78719
  const createSession = deps.createSession ?? defaultCreateSession;
78656
78720
  const version = runtimeVersion();
78657
78721
  const env = egressSessionEnv(envelope2, egress);
@@ -78659,7 +78723,9 @@ async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
78659
78723
  let session = null;
78660
78724
  let t8 = null;
78661
78725
  let timedOut = false;
78662
- const t7 = Date.now();
78726
+ let t7 = null;
78727
+ let sawRequest = false;
78728
+ const initStarted = performance.now();
78663
78729
  try {
78664
78730
  let tenantComponents;
78665
78731
  if (envelope2.tenantComponents?.length) {
@@ -78683,11 +78749,20 @@ async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
78683
78749
  ...onToolEvent ? { onToolEvent } : {}
78684
78750
  });
78685
78751
  session.subscribe((event) => {
78752
+ if (event.type === "model_request_started" && !sawRequest && typeof event.startedAt === "number") {
78753
+ t7 = event.startedAt;
78754
+ sawRequest = true;
78755
+ }
78756
+ if (event.type === "model_call_observed" && event.call) {
78757
+ if (observation.modelCalls.length < 128) observation.modelCalls.push(event.call);
78758
+ else observation.droppedModelCalls += 1;
78759
+ }
78686
78760
  if (timedOut || t8 !== null) return;
78687
78761
  if (event.type === "text_delta" && event.deltaKind === "text" && event.delta) {
78688
78762
  t8 = Date.now();
78689
78763
  }
78690
78764
  });
78765
+ observation.sessionInitMs = performance.now() - initStarted;
78691
78766
  const timer = setTimeout(() => {
78692
78767
  timedOut = true;
78693
78768
  void session?.interrupt().catch(() => void 0);
@@ -79383,7 +79458,7 @@ async function readResponseError(res) {
79383
79458
 
79384
79459
  // src/cli/index.ts
79385
79460
  init_ui();
79386
- var VERSION3 = "2.2.63";
79461
+ var VERSION3 = "2.2.64";
79387
79462
  function buildProgram() {
79388
79463
  const program = new import_commander32.Command("prismer").description("Prismer Runtime host and local control CLI (TS-only).").version(VERSION3).addHelpText(
79389
79464
  "after",
package/dist/cli.js CHANGED
@@ -2113,16 +2113,20 @@ function thinkingText(message) {
2113
2113
  }
2114
2114
  function mapUsage(usage) {
2115
2115
  if (!usage) return void 0;
2116
+ const measured = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
2116
2117
  return {
2117
- inputTokens: usage.input,
2118
- cachedInputTokens: usage.cacheRead,
2119
- outputTokens: usage.output,
2120
- totalCostUsd: usage.cost.total,
2121
- contextWindowUsedTokens: usage.totalTokens
2118
+ inputTokens: measured(usage.input),
2119
+ cachedInputTokens: measured(usage.cacheRead),
2120
+ cacheWriteTokens: measured(usage.cacheWrite),
2121
+ outputTokens: measured(usage.output),
2122
+ totalCostUsd: measured(usage.cost?.total),
2123
+ contextWindowUsedTokens: measured(usage.totalTokens)
2122
2124
  };
2123
2125
  }
2124
2126
  function extractAssistantUsage(message) {
2125
- return message.role === "assistant" ? mapUsage(message.usage) : void 0;
2127
+ if (message.role !== "assistant") return void 0;
2128
+ if ((message.stopReason === "error" || message.stopReason === "aborted") && ![message.usage?.input, message.usage?.output, message.usage?.cacheRead, message.usage?.cacheWrite].some((value) => typeof value === "number" && value > 0)) return void 0;
2129
+ return mapUsage(message.usage);
2126
2130
  }
2127
2131
  function modelDefinition(model) {
2128
2132
  return {
@@ -2683,7 +2687,15 @@ var init_agent = __esm({
2683
2687
  };
2684
2688
  })
2685
2689
  },
2686
- streamFn: options.models.streamSimple.bind(options.models),
2690
+ streamFn: (...args) => {
2691
+ const run = this.activeRun;
2692
+ if (run) {
2693
+ run.callCount = (run.callCount ?? 0) + 1;
2694
+ run.modelCall = { sequence: run.callCount, startedAt: Date.now(), clock: performance.now(), firstTextMs: null };
2695
+ this.emitObservation({ type: "model_request_started", provider: this.provider, startedAt: run.modelCall.startedAt, turnId: run.turnId });
2696
+ }
2697
+ return options.models.streamSimple(...args);
2698
+ },
2687
2699
  sessionId: this.id,
2688
2700
  convertToLlm: (messages) => messages.filter(isProviderMessage),
2689
2701
  toolExecution: "parallel",
@@ -2695,7 +2707,7 @@ var init_agent = __esm({
2695
2707
  // 「模型请求了一个不存在的工具」不会产生 tool_started——事件面只记真实执行。
2696
2708
  beforeToolCall: async (context, signal) => {
2697
2709
  if (sink) {
2698
- this.toolStartedAt.set(context.toolCall.id, Date.now());
2710
+ this.toolStartedAt.set(context.toolCall.id, performance.now());
2699
2711
  emitToolEvent(sink, {
2700
2712
  kind: "tool_started",
2701
2713
  name: context.toolCall.name,
@@ -2720,7 +2732,7 @@ var init_agent = __esm({
2720
2732
  name,
2721
2733
  isError: true,
2722
2734
  resultSummary: reason,
2723
- ...startedAt !== void 0 ? { durationMs: Date.now() - startedAt } : {}
2735
+ ...startedAt !== void 0 ? { durationMs: performance.now() - startedAt } : {}
2724
2736
  });
2725
2737
  return { block: true, reason };
2726
2738
  }
@@ -2735,7 +2747,7 @@ var init_agent = __esm({
2735
2747
  name: context.toolCall.name,
2736
2748
  resultSummary: summarizeToolResult(context.result, secrets),
2737
2749
  isError: context.isError,
2738
- ...startedAt !== void 0 ? { durationMs: Date.now() - startedAt } : {}
2750
+ ...startedAt !== void 0 ? { durationMs: performance.now() - startedAt } : {}
2739
2751
  });
2740
2752
  }
2741
2753
  return void 0;
@@ -2851,6 +2863,14 @@ var init_agent = __esm({
2851
2863
  emit(event) {
2852
2864
  for (const subscriber of this.subscribers) subscriber(event);
2853
2865
  }
2866
+ emitObservation(event) {
2867
+ for (const subscriber of this.subscribers) {
2868
+ try {
2869
+ void Promise.resolve(subscriber(event)).catch(() => void 0);
2870
+ } catch {
2871
+ }
2872
+ }
2873
+ }
2854
2874
  recordTimeline(item) {
2855
2875
  this.activeRun?.timeline.push(item);
2856
2876
  this.emit({
@@ -2870,6 +2890,9 @@ var init_agent = __esm({
2870
2890
  case "message_update": {
2871
2891
  const sub = event.assistantMessageEvent;
2872
2892
  if (sub.type === "text_delta" || sub.type === "thinking_delta") {
2893
+ if (sub.type === "text_delta" && sub.delta && run?.modelCall && run.modelCall.firstTextMs === null) {
2894
+ run.modelCall.firstTextMs = performance.now() - run.modelCall.clock;
2895
+ }
2873
2896
  this.emit({
2874
2897
  type: "text_delta",
2875
2898
  provider: this.provider,
@@ -2879,7 +2902,7 @@ var init_agent = __esm({
2879
2902
  });
2880
2903
  break;
2881
2904
  }
2882
- this.handleMessageEvent(event.message);
2905
+ this.handleMessageEvent(event.message, false);
2883
2906
  break;
2884
2907
  }
2885
2908
  case "message_end":
@@ -2917,7 +2940,7 @@ var init_agent = __esm({
2917
2940
  break;
2918
2941
  }
2919
2942
  }
2920
- handleMessageEvent(message) {
2943
+ handleMessageEvent(message, settled = true) {
2921
2944
  const run = this.activeRun;
2922
2945
  if (!run) return;
2923
2946
  if (message.role !== "assistant") return;
@@ -2930,8 +2953,34 @@ var init_agent = __esm({
2930
2953
  run.finalText = text2;
2931
2954
  this.recordTimeline({ type: "assistant_message", text: text2 });
2932
2955
  }
2933
- const usage = extractAssistantUsage(message);
2934
- if (usage) run.usage = usage;
2956
+ const usage = settled ? extractAssistantUsage(message) : void 0;
2957
+ if (settled && !usage) run.usage = {};
2958
+ if (usage) {
2959
+ const previous = run.usage;
2960
+ run.usage = { ...usage };
2961
+ for (const field2 of ["inputTokens", "outputTokens", "cachedInputTokens", "cacheWriteTokens", "totalCostUsd"]) {
2962
+ if (previous?.[field2] !== void 0 && usage[field2] !== void 0) {
2963
+ run.usage[field2] = previous[field2] + usage[field2];
2964
+ } else if (previous) {
2965
+ delete run.usage[field2];
2966
+ }
2967
+ }
2968
+ }
2969
+ if (settled && run.modelCall) {
2970
+ const call = run.modelCall;
2971
+ delete run.modelCall;
2972
+ this.emitObservation({ type: "model_call_observed", provider: this.provider, turnId: run.turnId, call: {
2973
+ sequence: call.sequence,
2974
+ source: "pi-engine",
2975
+ provider: message.provider,
2976
+ model: message.responseModel ?? message.model,
2977
+ startedAt: call.startedAt,
2978
+ durationMs: performance.now() - call.clock,
2979
+ firstTextMs: call.firstTextMs,
2980
+ status: message.stopReason === "error" ? "failed" : message.stopReason === "aborted" ? "canceled" : "completed",
2981
+ usage: usage ?? null
2982
+ } });
2983
+ }
2935
2984
  run.servedModel = message.responseModel ?? message.model;
2936
2985
  run.servedProvider = message.provider;
2937
2986
  }
@@ -16034,7 +16083,7 @@ var require_package = __commonJS({
16034
16083
  "package.json"(exports, module) {
16035
16084
  module.exports = {
16036
16085
  name: "@prismer/runtime",
16037
- version: "2.2.63",
16086
+ version: "2.2.64",
16038
16087
  description: "Prismer Cloud daemon runtime \u2014 TS-only adapter host for hosted IM agents",
16039
16088
  type: "module",
16040
16089
  main: "dist/index.js",
@@ -78696,6 +78745,21 @@ function classifyProviderError(message) {
78696
78745
  return /ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|fetch failed|socket hang up|network/i.test(message) ? "provider_unreachable" : "internal";
78697
78746
  }
78698
78747
  async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
78748
+ const started = performance.now();
78749
+ const observation = {
78750
+ version: 1,
78751
+ source: "runtime",
78752
+ sessionInitMs: null,
78753
+ totalMs: 0,
78754
+ modelCalls: [],
78755
+ droppedModelCalls: 0
78756
+ };
78757
+ const result = await runTurnExecution(envelope2, egress, deps, onToolEvent, observation);
78758
+ observation.totalMs = performance.now() - started;
78759
+ result.observation = observation;
78760
+ return result;
78761
+ }
78762
+ async function runTurnExecution(envelope2, egress, deps, onToolEvent, observation) {
78699
78763
  const createSession = deps.createSession ?? defaultCreateSession;
78700
78764
  const version = runtimeVersion();
78701
78765
  const env = egressSessionEnv(envelope2, egress);
@@ -78703,7 +78767,9 @@ async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
78703
78767
  let session = null;
78704
78768
  let t8 = null;
78705
78769
  let timedOut = false;
78706
- const t7 = Date.now();
78770
+ let t7 = null;
78771
+ let sawRequest = false;
78772
+ const initStarted = performance.now();
78707
78773
  try {
78708
78774
  let tenantComponents;
78709
78775
  if (envelope2.tenantComponents?.length) {
@@ -78727,11 +78793,20 @@ async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
78727
78793
  ...onToolEvent ? { onToolEvent } : {}
78728
78794
  });
78729
78795
  session.subscribe((event) => {
78796
+ if (event.type === "model_request_started" && !sawRequest && typeof event.startedAt === "number") {
78797
+ t7 = event.startedAt;
78798
+ sawRequest = true;
78799
+ }
78800
+ if (event.type === "model_call_observed" && event.call) {
78801
+ if (observation.modelCalls.length < 128) observation.modelCalls.push(event.call);
78802
+ else observation.droppedModelCalls += 1;
78803
+ }
78730
78804
  if (timedOut || t8 !== null) return;
78731
78805
  if (event.type === "text_delta" && event.deltaKind === "text" && event.delta) {
78732
78806
  t8 = Date.now();
78733
78807
  }
78734
78808
  });
78809
+ observation.sessionInitMs = performance.now() - initStarted;
78735
78810
  const timer = setTimeout(() => {
78736
78811
  timedOut = true;
78737
78812
  void session?.interrupt().catch(() => void 0);
@@ -79427,7 +79502,7 @@ async function readResponseError(res) {
79427
79502
 
79428
79503
  // src/cli/index.ts
79429
79504
  init_ui();
79430
- var VERSION3 = "2.2.63";
79505
+ var VERSION3 = "2.2.64";
79431
79506
  function buildProgram() {
79432
79507
  const program = new Command32("prismer").description("Prismer Runtime host and local control CLI (TS-only).").version(VERSION3).addHelpText(
79433
79508
  "after",
package/dist/index.cjs CHANGED
@@ -15707,16 +15707,20 @@ function thinkingText(message) {
15707
15707
  }
15708
15708
  function mapUsage(usage) {
15709
15709
  if (!usage) return void 0;
15710
+ const measured = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
15710
15711
  return {
15711
- inputTokens: usage.input,
15712
- cachedInputTokens: usage.cacheRead,
15713
- outputTokens: usage.output,
15714
- totalCostUsd: usage.cost.total,
15715
- contextWindowUsedTokens: usage.totalTokens
15712
+ inputTokens: measured(usage.input),
15713
+ cachedInputTokens: measured(usage.cacheRead),
15714
+ cacheWriteTokens: measured(usage.cacheWrite),
15715
+ outputTokens: measured(usage.output),
15716
+ totalCostUsd: measured(usage.cost?.total),
15717
+ contextWindowUsedTokens: measured(usage.totalTokens)
15716
15718
  };
15717
15719
  }
15718
15720
  function extractAssistantUsage(message) {
15719
- return message.role === "assistant" ? mapUsage(message.usage) : void 0;
15721
+ if (message.role !== "assistant") return void 0;
15722
+ if ((message.stopReason === "error" || message.stopReason === "aborted") && ![message.usage?.input, message.usage?.output, message.usage?.cacheRead, message.usage?.cacheWrite].some((value) => typeof value === "number" && value > 0)) return void 0;
15723
+ return mapUsage(message.usage);
15720
15724
  }
15721
15725
  function modelDefinition(model) {
15722
15726
  return {
@@ -16292,7 +16296,15 @@ var init_agent = __esm({
16292
16296
  };
16293
16297
  })
16294
16298
  },
16295
- streamFn: options.models.streamSimple.bind(options.models),
16299
+ streamFn: (...args) => {
16300
+ const run = this.activeRun;
16301
+ if (run) {
16302
+ run.callCount = (run.callCount ?? 0) + 1;
16303
+ run.modelCall = { sequence: run.callCount, startedAt: Date.now(), clock: performance.now(), firstTextMs: null };
16304
+ this.emitObservation({ type: "model_request_started", provider: this.provider, startedAt: run.modelCall.startedAt, turnId: run.turnId });
16305
+ }
16306
+ return options.models.streamSimple(...args);
16307
+ },
16296
16308
  sessionId: this.id,
16297
16309
  convertToLlm: (messages) => messages.filter(isProviderMessage),
16298
16310
  toolExecution: "parallel",
@@ -16304,7 +16316,7 @@ var init_agent = __esm({
16304
16316
  // 「模型请求了一个不存在的工具」不会产生 tool_started——事件面只记真实执行。
16305
16317
  beforeToolCall: async (context, signal) => {
16306
16318
  if (sink) {
16307
- this.toolStartedAt.set(context.toolCall.id, Date.now());
16319
+ this.toolStartedAt.set(context.toolCall.id, performance.now());
16308
16320
  emitToolEvent(sink, {
16309
16321
  kind: "tool_started",
16310
16322
  name: context.toolCall.name,
@@ -16329,7 +16341,7 @@ var init_agent = __esm({
16329
16341
  name,
16330
16342
  isError: true,
16331
16343
  resultSummary: reason,
16332
- ...startedAt !== void 0 ? { durationMs: Date.now() - startedAt } : {}
16344
+ ...startedAt !== void 0 ? { durationMs: performance.now() - startedAt } : {}
16333
16345
  });
16334
16346
  return { block: true, reason };
16335
16347
  }
@@ -16344,7 +16356,7 @@ var init_agent = __esm({
16344
16356
  name: context.toolCall.name,
16345
16357
  resultSummary: summarizeToolResult(context.result, secrets),
16346
16358
  isError: context.isError,
16347
- ...startedAt !== void 0 ? { durationMs: Date.now() - startedAt } : {}
16359
+ ...startedAt !== void 0 ? { durationMs: performance.now() - startedAt } : {}
16348
16360
  });
16349
16361
  }
16350
16362
  return void 0;
@@ -16460,6 +16472,14 @@ var init_agent = __esm({
16460
16472
  emit(event) {
16461
16473
  for (const subscriber of this.subscribers) subscriber(event);
16462
16474
  }
16475
+ emitObservation(event) {
16476
+ for (const subscriber of this.subscribers) {
16477
+ try {
16478
+ void Promise.resolve(subscriber(event)).catch(() => void 0);
16479
+ } catch {
16480
+ }
16481
+ }
16482
+ }
16463
16483
  recordTimeline(item) {
16464
16484
  this.activeRun?.timeline.push(item);
16465
16485
  this.emit({
@@ -16479,6 +16499,9 @@ var init_agent = __esm({
16479
16499
  case "message_update": {
16480
16500
  const sub = event.assistantMessageEvent;
16481
16501
  if (sub.type === "text_delta" || sub.type === "thinking_delta") {
16502
+ if (sub.type === "text_delta" && sub.delta && run?.modelCall && run.modelCall.firstTextMs === null) {
16503
+ run.modelCall.firstTextMs = performance.now() - run.modelCall.clock;
16504
+ }
16482
16505
  this.emit({
16483
16506
  type: "text_delta",
16484
16507
  provider: this.provider,
@@ -16488,7 +16511,7 @@ var init_agent = __esm({
16488
16511
  });
16489
16512
  break;
16490
16513
  }
16491
- this.handleMessageEvent(event.message);
16514
+ this.handleMessageEvent(event.message, false);
16492
16515
  break;
16493
16516
  }
16494
16517
  case "message_end":
@@ -16526,7 +16549,7 @@ var init_agent = __esm({
16526
16549
  break;
16527
16550
  }
16528
16551
  }
16529
- handleMessageEvent(message) {
16552
+ handleMessageEvent(message, settled = true) {
16530
16553
  const run = this.activeRun;
16531
16554
  if (!run) return;
16532
16555
  if (message.role !== "assistant") return;
@@ -16539,8 +16562,34 @@ var init_agent = __esm({
16539
16562
  run.finalText = text2;
16540
16563
  this.recordTimeline({ type: "assistant_message", text: text2 });
16541
16564
  }
16542
- const usage = extractAssistantUsage(message);
16543
- if (usage) run.usage = usage;
16565
+ const usage = settled ? extractAssistantUsage(message) : void 0;
16566
+ if (settled && !usage) run.usage = {};
16567
+ if (usage) {
16568
+ const previous = run.usage;
16569
+ run.usage = { ...usage };
16570
+ for (const field2 of ["inputTokens", "outputTokens", "cachedInputTokens", "cacheWriteTokens", "totalCostUsd"]) {
16571
+ if (previous?.[field2] !== void 0 && usage[field2] !== void 0) {
16572
+ run.usage[field2] = previous[field2] + usage[field2];
16573
+ } else if (previous) {
16574
+ delete run.usage[field2];
16575
+ }
16576
+ }
16577
+ }
16578
+ if (settled && run.modelCall) {
16579
+ const call = run.modelCall;
16580
+ delete run.modelCall;
16581
+ this.emitObservation({ type: "model_call_observed", provider: this.provider, turnId: run.turnId, call: {
16582
+ sequence: call.sequence,
16583
+ source: "pi-engine",
16584
+ provider: message.provider,
16585
+ model: message.responseModel ?? message.model,
16586
+ startedAt: call.startedAt,
16587
+ durationMs: performance.now() - call.clock,
16588
+ firstTextMs: call.firstTextMs,
16589
+ status: message.stopReason === "error" ? "failed" : message.stopReason === "aborted" ? "canceled" : "completed",
16590
+ usage: usage ?? null
16591
+ } });
16592
+ }
16544
16593
  run.servedModel = message.responseModel ?? message.model;
16545
16594
  run.servedProvider = message.provider;
16546
16595
  }
@@ -17065,7 +17114,7 @@ var require_package = __commonJS({
17065
17114
  "package.json"(exports2, module2) {
17066
17115
  module2.exports = {
17067
17116
  name: "@prismer/runtime",
17068
- version: "2.2.63",
17117
+ version: "2.2.64",
17069
17118
  description: "Prismer Cloud daemon runtime \u2014 TS-only adapter host for hosted IM agents",
17070
17119
  type: "module",
17071
17120
  main: "dist/index.js",
@@ -79256,6 +79305,21 @@ function classifyProviderError(message) {
79256
79305
  return /ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|fetch failed|socket hang up|network/i.test(message) ? "provider_unreachable" : "internal";
79257
79306
  }
79258
79307
  async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
79308
+ const started = performance.now();
79309
+ const observation = {
79310
+ version: 1,
79311
+ source: "runtime",
79312
+ sessionInitMs: null,
79313
+ totalMs: 0,
79314
+ modelCalls: [],
79315
+ droppedModelCalls: 0
79316
+ };
79317
+ const result = await runTurnExecution(envelope2, egress, deps, onToolEvent, observation);
79318
+ observation.totalMs = performance.now() - started;
79319
+ result.observation = observation;
79320
+ return result;
79321
+ }
79322
+ async function runTurnExecution(envelope2, egress, deps, onToolEvent, observation) {
79259
79323
  const createSession = deps.createSession ?? defaultCreateSession;
79260
79324
  const version = runtimeVersion();
79261
79325
  const env = egressSessionEnv(envelope2, egress);
@@ -79263,7 +79327,9 @@ async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
79263
79327
  let session = null;
79264
79328
  let t8 = null;
79265
79329
  let timedOut = false;
79266
- const t7 = Date.now();
79330
+ let t7 = null;
79331
+ let sawRequest = false;
79332
+ const initStarted = performance.now();
79267
79333
  try {
79268
79334
  let tenantComponents;
79269
79335
  if (envelope2.tenantComponents?.length) {
@@ -79287,11 +79353,20 @@ async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
79287
79353
  ...onToolEvent ? { onToolEvent } : {}
79288
79354
  });
79289
79355
  session.subscribe((event) => {
79356
+ if (event.type === "model_request_started" && !sawRequest && typeof event.startedAt === "number") {
79357
+ t7 = event.startedAt;
79358
+ sawRequest = true;
79359
+ }
79360
+ if (event.type === "model_call_observed" && event.call) {
79361
+ if (observation.modelCalls.length < 128) observation.modelCalls.push(event.call);
79362
+ else observation.droppedModelCalls += 1;
79363
+ }
79290
79364
  if (timedOut || t8 !== null) return;
79291
79365
  if (event.type === "text_delta" && event.deltaKind === "text" && event.delta) {
79292
79366
  t8 = Date.now();
79293
79367
  }
79294
79368
  });
79369
+ observation.sessionInitMs = performance.now() - initStarted;
79295
79370
  const timer = setTimeout(() => {
79296
79371
  timedOut = true;
79297
79372
  void session?.interrupt().catch(() => void 0);
@@ -79987,7 +80062,7 @@ async function readResponseError(res) {
79987
80062
 
79988
80063
  // src/cli/index.ts
79989
80064
  init_ui();
79990
- var VERSION3 = "2.2.63";
80065
+ var VERSION3 = "2.2.64";
79991
80066
  function buildProgram() {
79992
80067
  const program = new import_commander32.Command("prismer").description("Prismer Runtime host and local control CLI (TS-only).").version(VERSION3).addHelpText(
79993
80068
  "after",
package/dist/index.d.cts CHANGED
@@ -1875,11 +1875,25 @@ interface AgentRunOptions {
1875
1875
  interface AgentUsage {
1876
1876
  inputTokens?: number;
1877
1877
  cachedInputTokens?: number;
1878
+ /** PI cache creation tokens, separate from uncached input and cache reads. */
1879
+ cacheWriteTokens?: number;
1878
1880
  outputTokens?: number;
1879
1881
  totalCostUsd?: number;
1880
1882
  contextWindowMaxTokens?: number;
1881
1883
  contextWindowUsedTokens?: number;
1882
1884
  }
1885
+ /** Measurements at the PI engine boundary, not a provider billing receipt. */
1886
+ interface ModelCallObservation {
1887
+ sequence: number;
1888
+ source: 'pi-engine';
1889
+ provider: string;
1890
+ model: string;
1891
+ startedAt: number;
1892
+ durationMs: number;
1893
+ firstTextMs: number | null;
1894
+ status: 'completed' | 'failed' | 'canceled';
1895
+ usage: AgentUsage | null;
1896
+ }
1883
1897
  declare const TOOL_CALL_ICON_NAMES: readonly ["wrench", "square_terminal", "eye", "pencil", "search", "bot", "sparkles", "brain", "mic_vocal"];
1884
1898
  type ToolCallIconName = (typeof TOOL_CALL_ICON_NAMES)[number];
1885
1899
  type ToolCallDetail = {
@@ -2032,6 +2046,16 @@ type AgentTimelineItem = {
2032
2046
  message: string;
2033
2047
  } | CompactionTimelineItem;
2034
2048
  type AgentStreamEvent = {
2049
+ type: 'model_request_started';
2050
+ provider: AgentProvider;
2051
+ startedAt: number;
2052
+ turnId?: string;
2053
+ } | {
2054
+ type: 'model_call_observed';
2055
+ provider: AgentProvider;
2056
+ call: ModelCallObservation;
2057
+ turnId?: string;
2058
+ } | {
2035
2059
  type: "thread_started";
2036
2060
  sessionId: string;
2037
2061
  provider: AgentProvider;
@@ -7260,6 +7284,7 @@ declare class PiAgentCoreSession implements AgentSession {
7260
7284
  close(): Promise<void>;
7261
7285
  listCommands(): Promise<AgentSlashCommand[]>;
7262
7286
  private emit;
7287
+ private emitObservation;
7263
7288
  private recordTimeline;
7264
7289
  private handlePiEvent;
7265
7290
  private handleMessageEvent;
package/dist/index.d.ts CHANGED
@@ -1875,11 +1875,25 @@ interface AgentRunOptions {
1875
1875
  interface AgentUsage {
1876
1876
  inputTokens?: number;
1877
1877
  cachedInputTokens?: number;
1878
+ /** PI cache creation tokens, separate from uncached input and cache reads. */
1879
+ cacheWriteTokens?: number;
1878
1880
  outputTokens?: number;
1879
1881
  totalCostUsd?: number;
1880
1882
  contextWindowMaxTokens?: number;
1881
1883
  contextWindowUsedTokens?: number;
1882
1884
  }
1885
+ /** Measurements at the PI engine boundary, not a provider billing receipt. */
1886
+ interface ModelCallObservation {
1887
+ sequence: number;
1888
+ source: 'pi-engine';
1889
+ provider: string;
1890
+ model: string;
1891
+ startedAt: number;
1892
+ durationMs: number;
1893
+ firstTextMs: number | null;
1894
+ status: 'completed' | 'failed' | 'canceled';
1895
+ usage: AgentUsage | null;
1896
+ }
1883
1897
  declare const TOOL_CALL_ICON_NAMES: readonly ["wrench", "square_terminal", "eye", "pencil", "search", "bot", "sparkles", "brain", "mic_vocal"];
1884
1898
  type ToolCallIconName = (typeof TOOL_CALL_ICON_NAMES)[number];
1885
1899
  type ToolCallDetail = {
@@ -2032,6 +2046,16 @@ type AgentTimelineItem = {
2032
2046
  message: string;
2033
2047
  } | CompactionTimelineItem;
2034
2048
  type AgentStreamEvent = {
2049
+ type: 'model_request_started';
2050
+ provider: AgentProvider;
2051
+ startedAt: number;
2052
+ turnId?: string;
2053
+ } | {
2054
+ type: 'model_call_observed';
2055
+ provider: AgentProvider;
2056
+ call: ModelCallObservation;
2057
+ turnId?: string;
2058
+ } | {
2035
2059
  type: "thread_started";
2036
2060
  sessionId: string;
2037
2061
  provider: AgentProvider;
@@ -7260,6 +7284,7 @@ declare class PiAgentCoreSession implements AgentSession {
7260
7284
  close(): Promise<void>;
7261
7285
  listCommands(): Promise<AgentSlashCommand[]>;
7262
7286
  private emit;
7287
+ private emitObservation;
7263
7288
  private recordTimeline;
7264
7289
  private handlePiEvent;
7265
7290
  private handleMessageEvent;
package/dist/index.js CHANGED
@@ -15752,16 +15752,20 @@ function thinkingText(message) {
15752
15752
  }
15753
15753
  function mapUsage(usage) {
15754
15754
  if (!usage) return void 0;
15755
+ const measured = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
15755
15756
  return {
15756
- inputTokens: usage.input,
15757
- cachedInputTokens: usage.cacheRead,
15758
- outputTokens: usage.output,
15759
- totalCostUsd: usage.cost.total,
15760
- contextWindowUsedTokens: usage.totalTokens
15757
+ inputTokens: measured(usage.input),
15758
+ cachedInputTokens: measured(usage.cacheRead),
15759
+ cacheWriteTokens: measured(usage.cacheWrite),
15760
+ outputTokens: measured(usage.output),
15761
+ totalCostUsd: measured(usage.cost?.total),
15762
+ contextWindowUsedTokens: measured(usage.totalTokens)
15761
15763
  };
15762
15764
  }
15763
15765
  function extractAssistantUsage(message) {
15764
- return message.role === "assistant" ? mapUsage(message.usage) : void 0;
15766
+ if (message.role !== "assistant") return void 0;
15767
+ if ((message.stopReason === "error" || message.stopReason === "aborted") && ![message.usage?.input, message.usage?.output, message.usage?.cacheRead, message.usage?.cacheWrite].some((value) => typeof value === "number" && value > 0)) return void 0;
15768
+ return mapUsage(message.usage);
15765
15769
  }
15766
15770
  function modelDefinition(model) {
15767
15771
  return {
@@ -16322,7 +16326,15 @@ var init_agent = __esm({
16322
16326
  };
16323
16327
  })
16324
16328
  },
16325
- streamFn: options.models.streamSimple.bind(options.models),
16329
+ streamFn: (...args) => {
16330
+ const run = this.activeRun;
16331
+ if (run) {
16332
+ run.callCount = (run.callCount ?? 0) + 1;
16333
+ run.modelCall = { sequence: run.callCount, startedAt: Date.now(), clock: performance.now(), firstTextMs: null };
16334
+ this.emitObservation({ type: "model_request_started", provider: this.provider, startedAt: run.modelCall.startedAt, turnId: run.turnId });
16335
+ }
16336
+ return options.models.streamSimple(...args);
16337
+ },
16326
16338
  sessionId: this.id,
16327
16339
  convertToLlm: (messages) => messages.filter(isProviderMessage),
16328
16340
  toolExecution: "parallel",
@@ -16334,7 +16346,7 @@ var init_agent = __esm({
16334
16346
  // 「模型请求了一个不存在的工具」不会产生 tool_started——事件面只记真实执行。
16335
16347
  beforeToolCall: async (context, signal) => {
16336
16348
  if (sink) {
16337
- this.toolStartedAt.set(context.toolCall.id, Date.now());
16349
+ this.toolStartedAt.set(context.toolCall.id, performance.now());
16338
16350
  emitToolEvent(sink, {
16339
16351
  kind: "tool_started",
16340
16352
  name: context.toolCall.name,
@@ -16359,7 +16371,7 @@ var init_agent = __esm({
16359
16371
  name,
16360
16372
  isError: true,
16361
16373
  resultSummary: reason,
16362
- ...startedAt !== void 0 ? { durationMs: Date.now() - startedAt } : {}
16374
+ ...startedAt !== void 0 ? { durationMs: performance.now() - startedAt } : {}
16363
16375
  });
16364
16376
  return { block: true, reason };
16365
16377
  }
@@ -16374,7 +16386,7 @@ var init_agent = __esm({
16374
16386
  name: context.toolCall.name,
16375
16387
  resultSummary: summarizeToolResult(context.result, secrets),
16376
16388
  isError: context.isError,
16377
- ...startedAt !== void 0 ? { durationMs: Date.now() - startedAt } : {}
16389
+ ...startedAt !== void 0 ? { durationMs: performance.now() - startedAt } : {}
16378
16390
  });
16379
16391
  }
16380
16392
  return void 0;
@@ -16490,6 +16502,14 @@ var init_agent = __esm({
16490
16502
  emit(event) {
16491
16503
  for (const subscriber of this.subscribers) subscriber(event);
16492
16504
  }
16505
+ emitObservation(event) {
16506
+ for (const subscriber of this.subscribers) {
16507
+ try {
16508
+ void Promise.resolve(subscriber(event)).catch(() => void 0);
16509
+ } catch {
16510
+ }
16511
+ }
16512
+ }
16493
16513
  recordTimeline(item) {
16494
16514
  this.activeRun?.timeline.push(item);
16495
16515
  this.emit({
@@ -16509,6 +16529,9 @@ var init_agent = __esm({
16509
16529
  case "message_update": {
16510
16530
  const sub = event.assistantMessageEvent;
16511
16531
  if (sub.type === "text_delta" || sub.type === "thinking_delta") {
16532
+ if (sub.type === "text_delta" && sub.delta && run?.modelCall && run.modelCall.firstTextMs === null) {
16533
+ run.modelCall.firstTextMs = performance.now() - run.modelCall.clock;
16534
+ }
16512
16535
  this.emit({
16513
16536
  type: "text_delta",
16514
16537
  provider: this.provider,
@@ -16518,7 +16541,7 @@ var init_agent = __esm({
16518
16541
  });
16519
16542
  break;
16520
16543
  }
16521
- this.handleMessageEvent(event.message);
16544
+ this.handleMessageEvent(event.message, false);
16522
16545
  break;
16523
16546
  }
16524
16547
  case "message_end":
@@ -16556,7 +16579,7 @@ var init_agent = __esm({
16556
16579
  break;
16557
16580
  }
16558
16581
  }
16559
- handleMessageEvent(message) {
16582
+ handleMessageEvent(message, settled = true) {
16560
16583
  const run = this.activeRun;
16561
16584
  if (!run) return;
16562
16585
  if (message.role !== "assistant") return;
@@ -16569,8 +16592,34 @@ var init_agent = __esm({
16569
16592
  run.finalText = text2;
16570
16593
  this.recordTimeline({ type: "assistant_message", text: text2 });
16571
16594
  }
16572
- const usage = extractAssistantUsage(message);
16573
- if (usage) run.usage = usage;
16595
+ const usage = settled ? extractAssistantUsage(message) : void 0;
16596
+ if (settled && !usage) run.usage = {};
16597
+ if (usage) {
16598
+ const previous = run.usage;
16599
+ run.usage = { ...usage };
16600
+ for (const field2 of ["inputTokens", "outputTokens", "cachedInputTokens", "cacheWriteTokens", "totalCostUsd"]) {
16601
+ if (previous?.[field2] !== void 0 && usage[field2] !== void 0) {
16602
+ run.usage[field2] = previous[field2] + usage[field2];
16603
+ } else if (previous) {
16604
+ delete run.usage[field2];
16605
+ }
16606
+ }
16607
+ }
16608
+ if (settled && run.modelCall) {
16609
+ const call = run.modelCall;
16610
+ delete run.modelCall;
16611
+ this.emitObservation({ type: "model_call_observed", provider: this.provider, turnId: run.turnId, call: {
16612
+ sequence: call.sequence,
16613
+ source: "pi-engine",
16614
+ provider: message.provider,
16615
+ model: message.responseModel ?? message.model,
16616
+ startedAt: call.startedAt,
16617
+ durationMs: performance.now() - call.clock,
16618
+ firstTextMs: call.firstTextMs,
16619
+ status: message.stopReason === "error" ? "failed" : message.stopReason === "aborted" ? "canceled" : "completed",
16620
+ usage: usage ?? null
16621
+ } });
16622
+ }
16574
16623
  run.servedModel = message.responseModel ?? message.model;
16575
16624
  run.servedProvider = message.provider;
16576
16625
  }
@@ -17094,7 +17143,7 @@ var require_package = __commonJS({
17094
17143
  "package.json"(exports, module) {
17095
17144
  module.exports = {
17096
17145
  name: "@prismer/runtime",
17097
- version: "2.2.63",
17146
+ version: "2.2.64",
17098
17147
  description: "Prismer Cloud daemon runtime \u2014 TS-only adapter host for hosted IM agents",
17099
17148
  type: "module",
17100
17149
  main: "dist/index.js",
@@ -79189,6 +79238,21 @@ function classifyProviderError(message) {
79189
79238
  return /ENOTFOUND|ECONNREFUSED|ETIMEDOUT|EAI_AGAIN|fetch failed|socket hang up|network/i.test(message) ? "provider_unreachable" : "internal";
79190
79239
  }
79191
79240
  async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
79241
+ const started = performance.now();
79242
+ const observation = {
79243
+ version: 1,
79244
+ source: "runtime",
79245
+ sessionInitMs: null,
79246
+ totalMs: 0,
79247
+ modelCalls: [],
79248
+ droppedModelCalls: 0
79249
+ };
79250
+ const result = await runTurnExecution(envelope2, egress, deps, onToolEvent, observation);
79251
+ observation.totalMs = performance.now() - started;
79252
+ result.observation = observation;
79253
+ return result;
79254
+ }
79255
+ async function runTurnExecution(envelope2, egress, deps, onToolEvent, observation) {
79192
79256
  const createSession = deps.createSession ?? defaultCreateSession;
79193
79257
  const version = runtimeVersion();
79194
79258
  const env = egressSessionEnv(envelope2, egress);
@@ -79196,7 +79260,9 @@ async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
79196
79260
  let session = null;
79197
79261
  let t8 = null;
79198
79262
  let timedOut = false;
79199
- const t7 = Date.now();
79263
+ let t7 = null;
79264
+ let sawRequest = false;
79265
+ const initStarted = performance.now();
79200
79266
  try {
79201
79267
  let tenantComponents;
79202
79268
  if (envelope2.tenantComponents?.length) {
@@ -79220,11 +79286,20 @@ async function runTurnOnce(envelope2, egress, deps = {}, onToolEvent) {
79220
79286
  ...onToolEvent ? { onToolEvent } : {}
79221
79287
  });
79222
79288
  session.subscribe((event) => {
79289
+ if (event.type === "model_request_started" && !sawRequest && typeof event.startedAt === "number") {
79290
+ t7 = event.startedAt;
79291
+ sawRequest = true;
79292
+ }
79293
+ if (event.type === "model_call_observed" && event.call) {
79294
+ if (observation.modelCalls.length < 128) observation.modelCalls.push(event.call);
79295
+ else observation.droppedModelCalls += 1;
79296
+ }
79223
79297
  if (timedOut || t8 !== null) return;
79224
79298
  if (event.type === "text_delta" && event.deltaKind === "text" && event.delta) {
79225
79299
  t8 = Date.now();
79226
79300
  }
79227
79301
  });
79302
+ observation.sessionInitMs = performance.now() - initStarted;
79228
79303
  const timer = setTimeout(() => {
79229
79304
  timedOut = true;
79230
79305
  void session?.interrupt().catch(() => void 0);
@@ -79920,7 +79995,7 @@ async function readResponseError(res) {
79920
79995
 
79921
79996
  // src/cli/index.ts
79922
79997
  init_ui();
79923
- var VERSION3 = "2.2.63";
79998
+ var VERSION3 = "2.2.64";
79924
79999
  function buildProgram() {
79925
80000
  const program = new Command32("prismer").description("Prismer Runtime host and local control CLI (TS-only).").version(VERSION3).addHelpText(
79926
80001
  "after",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prismer/runtime",
3
- "version": "2.2.63",
3
+ "version": "2.2.64",
4
4
  "description": "Prismer Cloud daemon runtime — TS-only adapter host for hosted IM agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",