@moxt-ai/mobius 0.0.347 → 0.0.348

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.
Files changed (28) hide show
  1. package/dist/cli.js +153 -98
  2. package/dist/cli.js.map +4 -4
  3. package/dist/engine/runtime/command/registry/prompt.js +4 -4
  4. package/dist/engine/runtime/command/registry/prompt.js.map +1 -1
  5. package/dist/engine/runtime/command/registry/session.d.ts.map +1 -1
  6. package/dist/engine/runtime/command/registry/session.js +8 -1
  7. package/dist/engine/runtime/command/registry/session.js.map +1 -1
  8. package/dist/engine/runtime/relay/session-event-publisher.d.ts.map +1 -1
  9. package/dist/engine/runtime/relay/session-event-publisher.js +22 -1
  10. package/dist/engine/runtime/relay/session-event-publisher.js.map +1 -1
  11. package/dist/engine/session/content/external-content-store.d.ts +4 -1
  12. package/dist/engine/session/content/external-content-store.d.ts.map +1 -1
  13. package/dist/engine/session/content/external-content-store.js +11 -3
  14. package/dist/engine/session/content/external-content-store.js.map +1 -1
  15. package/dist/engine/session/prompt/prompt-artifacts.d.ts +6 -1
  16. package/dist/engine/session/prompt/prompt-artifacts.d.ts.map +1 -1
  17. package/dist/engine/session/prompt/prompt-artifacts.js +12 -17
  18. package/dist/engine/session/prompt/prompt-artifacts.js.map +1 -1
  19. package/dist/engine/session/prompt/prompt-lifecycle.d.ts.map +1 -1
  20. package/dist/engine/session/prompt/prompt-lifecycle.js +2 -1
  21. package/dist/engine/session/prompt/prompt-lifecycle.js.map +1 -1
  22. package/dist/engine/session/workspace/workspace-directory.d.ts +1 -0
  23. package/dist/engine/session/workspace/workspace-directory.d.ts.map +1 -1
  24. package/dist/engine/session/workspace/workspace-directory.js +2 -1
  25. package/dist/engine/session/workspace/workspace-directory.js.map +1 -1
  26. package/dist/engine.js +144 -90
  27. package/dist/engine.js.map +4 -4
  28. package/package.json +6 -6
package/dist/cli.js CHANGED
@@ -2086,7 +2086,7 @@ var init_package = __esm({
2086
2086
  "package.json"() {
2087
2087
  package_default = {
2088
2088
  name: "@moxt-ai/mobius",
2089
- version: "0.0.347",
2089
+ version: "0.0.348",
2090
2090
  description: "Mobius CLI for pairing and running local AI agent services.",
2091
2091
  type: "module",
2092
2092
  bin: {
@@ -2128,13 +2128,13 @@ var init_package = __esm({
2128
2128
  prepack: "pnpm build",
2129
2129
  start: "node dist/cli.js",
2130
2130
  typecheck: "tsc --project tsconfig.json && tsc --project tests/tsconfig.json",
2131
- "pretest:controlled": "tsc --project src/engine/acp/harness/claude/upstream/tsconfig.json && tsc --project src/engine/acp/harness/pi/upstream/tsconfig.json",
2132
- "test:controlled": "vitest run tests/sessions tests/runtime --maxWorkers=4",
2131
+ "pretest:controlled": "pnpm build",
2132
+ "test:controlled": "vitest run tests/sessions tests/runtime",
2133
2133
  "pretest:manual": "pnpm pretest:controlled",
2134
2134
  "test:manual": "vitest run tests/manual",
2135
- "pretest:agents": "tsc --project src/engine/acp/harness/claude/upstream/tsconfig.json && tsc --project src/engine/acp/harness/pi/upstream/tsconfig.json",
2135
+ "pretest:agents": "pnpm build",
2136
2136
  "test:agents": "vitest run tests/agents --maxWorkers=2",
2137
- "pretest:npm-compatibility": "tsc --project src/engine/acp/harness/claude/upstream/tsconfig.json && tsc --project src/engine/acp/harness/pi/upstream/tsconfig.json",
2137
+ "pretest:npm-compatibility": "pnpm build",
2138
2138
  "test:npm-compatibility": "vitest run tests/runtime/daemon-upgrade.integration.test.ts",
2139
2139
  "pretest:pressure": "tsc --project src/engine/acp/harness/claude/upstream/tsconfig.json && tsc --project src/engine/acp/harness/pi/upstream/tsconfig.json",
2140
2140
  "test:pressure": "node tests/pressure/concurrent-sessions.mjs"
@@ -28217,7 +28217,8 @@ var init_workspace = __esm({
28217
28217
  bindingId: identifierStringSchema,
28218
28218
  directoryId: DIRECTORY_IDENTIFIER,
28219
28219
  kind: external_exports.literal(WORKSPACE_LIST_KIND),
28220
- requestId: identifierStringSchema
28220
+ requestId: identifierStringSchema,
28221
+ view: external_exports.literal("metadata").optional()
28221
28222
  }).readonly();
28222
28223
  workspaceEntriesEventSchema = external_exports.object({
28223
28224
  bindingId: identifierStringSchema,
@@ -71196,6 +71197,7 @@ var init_acp_agent = __esm({
71196
71197
  session.titles.onPrompt(params.prompt);
71197
71198
  const turn = {
71198
71199
  promptUuid,
71200
+ inputQueuedAt: performance.now(),
71199
71201
  isLocalOnlyCommand,
71200
71202
  settled: false,
71201
71203
  resolve: () => {
@@ -71681,6 +71683,15 @@ var init_acp_agent = __esm({
71681
71683
  case "started": {
71682
71684
  const queued = findUnsettledTurn(frame.command_uuid);
71683
71685
  if (queued) {
71686
+ if (!queued.commandStarted) {
71687
+ this.logger.log(JSON.stringify({
71688
+ event: "claude-sdk-command-start-timing",
71689
+ sessionId: params.sessionId,
71690
+ promptUuid: queued.promptUuid,
71691
+ observedAt: Date.now(),
71692
+ queuedToCommandStartMs: performance.now() - queued.inputQueuedAt
71693
+ }));
71694
+ }
71684
71695
  queued.commandStarted = true;
71685
71696
  }
71686
71697
  const state = session.orphanCommands?.get(frame.command_uuid);
@@ -72367,6 +72378,16 @@ ${message.api_refusal_explanation}` : "";
72367
72378
  }
72368
72379
  const queued = findUnsettledTurn(message.uuid);
72369
72380
  if (queued) {
72381
+ if (!queued.inputEchoObserved) {
72382
+ queued.inputEchoObserved = true;
72383
+ this.logger.log(JSON.stringify({
72384
+ event: "claude-sdk-input-echo-timing",
72385
+ sessionId: params.sessionId,
72386
+ promptUuid: queued.promptUuid,
72387
+ observedAt: Date.now(),
72388
+ queuedToEchoMs: performance.now() - queued.inputQueuedAt
72389
+ }));
72390
+ }
72370
72391
  if (session.activeTurn !== queued) {
72371
72392
  if (session.activeTurn) {
72372
72393
  if (session.cancelled) {
@@ -95134,6 +95155,71 @@ var init_native_session_browser = __esm({
95134
95155
  }
95135
95156
  });
95136
95157
 
95158
+ // src/engine/session/prompt/prompt-artifacts.ts
95159
+ import { createHash as createHash6 } from "node:crypto";
95160
+ import { pathToFileURL as pathToFileURL2 } from "node:url";
95161
+ function isTextPromptResourceMediaType(mediaType) {
95162
+ return mediaType.startsWith("text/") || mediaType === "application/json" || mediaType.endsWith("+json") || mediaType === "application/xml" || mediaType.endsWith("+xml");
95163
+ }
95164
+ async function resolvePromptArtifacts(read, attachments, signal) {
95165
+ const images = [];
95166
+ const resources = [];
95167
+ for (const attachment of attachments) {
95168
+ signal.throwIfAborted();
95169
+ const artifact = await read(attachment, signal);
95170
+ const { bytes } = artifact;
95171
+ if (bytes.byteLength !== attachment.byteLength || createHash6("sha256").update(bytes).digest("hex") !== attachment.contentKey) {
95172
+ throw new PromptArtifactError(
95173
+ "artifact_integrity_failed",
95174
+ "The prompt attachment failed integrity validation"
95175
+ );
95176
+ }
95177
+ if (attachment.kind === "image") {
95178
+ if (bytes.byteLength > PROMPT_IMAGE_MAX_BYTES || images.length >= 8) {
95179
+ continue;
95180
+ }
95181
+ images.push({
95182
+ data: Buffer.from(bytes).toString("base64"),
95183
+ mimeType: attachment.mimeType,
95184
+ name: attachment.name
95185
+ });
95186
+ continue;
95187
+ }
95188
+ if (bytes.byteLength > MAX_PROMPT_ATTACHMENT_BYTES || resources.length >= 16) {
95189
+ continue;
95190
+ }
95191
+ resources.push({
95192
+ mimeType: attachment.mimeType,
95193
+ name: attachment.name,
95194
+ type: "link",
95195
+ uri: pathToFileURL2(artifact.path).href
95196
+ });
95197
+ }
95198
+ return new ResolvedPromptArtifacts(images, resources);
95199
+ }
95200
+ var PromptArtifactError, ResolvedPromptArtifacts;
95201
+ var init_prompt_artifacts = __esm({
95202
+ "src/engine/session/prompt/prompt-artifacts.ts"() {
95203
+ "use strict";
95204
+ init_protocol();
95205
+ init_operation_error();
95206
+ PromptArtifactError = class extends OperationError {
95207
+ constructor(code, message, options = {}) {
95208
+ super(code, message, options);
95209
+ this.name = "PromptArtifactError";
95210
+ }
95211
+ };
95212
+ ResolvedPromptArtifacts = class {
95213
+ images;
95214
+ resources;
95215
+ constructor(images, resources) {
95216
+ this.images = images;
95217
+ this.resources = resources;
95218
+ }
95219
+ };
95220
+ }
95221
+ });
95222
+
95137
95223
  // src/engine/session/prompt/prompt-lifecycle.ts
95138
95224
  function promptContent(command) {
95139
95225
  const content = [];
@@ -95150,7 +95236,7 @@ function promptContent(command) {
95150
95236
  uri: attachment.uri
95151
95237
  } : {
95152
95238
  byteLength: attachment.byteLength,
95153
- encoding: "text",
95239
+ encoding: isTextPromptResourceMediaType(attachment.mimeType) ? "text" : "binary",
95154
95240
  mimeType: attachment.mimeType,
95155
95241
  resourceKey: attachment.contentKey,
95156
95242
  type: "external_resource",
@@ -95184,6 +95270,7 @@ var init_prompt_lifecycle = __esm({
95184
95270
  "src/engine/session/prompt/prompt-lifecycle.ts"() {
95185
95271
  "use strict";
95186
95272
  init_protocol();
95273
+ init_prompt_artifacts();
95187
95274
  PromptLifecycle = class {
95188
95275
  #sink;
95189
95276
  #terminal = /* @__PURE__ */ new Map();
@@ -95322,7 +95409,7 @@ var init_prompt_lifecycle = __esm({
95322
95409
  });
95323
95410
 
95324
95411
  // src/engine/session/turn/execution-receipts.ts
95325
- import { createHash as createHash6, randomUUID as randomUUID15 } from "node:crypto";
95412
+ import { createHash as createHash7, randomUUID as randomUUID15 } from "node:crypto";
95326
95413
  var MAX_TERMINAL_RECEIPTS, ExecutionReceipts;
95327
95414
  var init_execution_receipts = __esm({
95328
95415
  "src/engine/session/turn/execution-receipts.ts"() {
@@ -95440,7 +95527,7 @@ var init_execution_receipts = __esm({
95440
95527
  type: "lifecycle-metadata",
95441
95528
  payload: { kind: "prompt.completed", commandId, stopReason: "cancelled" }
95442
95529
  };
95443
- const commandHash = createHash6("sha256").update(
95530
+ const commandHash = createHash7("sha256").update(
95444
95531
  encodeSessionCancelBeforeStartCommand({
95445
95532
  commandId,
95446
95533
  kind: "session.cancel-before-start",
@@ -95504,7 +95591,7 @@ var init_execution_receipts = __esm({
95504
95591
  return recovered;
95505
95592
  };
95506
95593
  claimExecution = (commandId, sessionId, commandJson) => {
95507
- const commandHash = createHash6("sha256").update(commandJson).digest("hex");
95594
+ const commandHash = createHash7("sha256").update(commandJson).digest("hex");
95508
95595
  let existing = this.#executions.get(commandId);
95509
95596
  const previous = this.outcome(commandId);
95510
95597
  if (existing?.sessionId === sessionId && !existing.started && previous?.type === "lifecycle-metadata" && previous.payload.kind === "prompt.failed-before-start" && previous.payload.code === "runtime_restarted") {
@@ -96091,7 +96178,8 @@ var init_workspace_directory = __esm({
96091
96178
  };
96092
96179
  describe = async (bindingId, id, signal) => {
96093
96180
  const path8 = await this.resolve(bindingId, id, signal);
96094
- return { path: path8, label: this.label(path8) };
96181
+ const parent = dirname11(path8);
96182
+ return { path: path8, label: this.label(path8), parentDirectoryId: parent === path8 ? "" : encodePath(parent) };
96095
96183
  };
96096
96184
  list = async (bindingId, id, signal) => {
96097
96185
  const target = await this.resolve(bindingId, id, signal);
@@ -96368,8 +96456,12 @@ var init_scratch_directory = __esm({
96368
96456
  });
96369
96457
 
96370
96458
  // src/engine/session/content/external-content-store.ts
96371
- import { createHash as createHash7 } from "node:crypto";
96372
- import { join as join28 } from "node:path";
96459
+ import { createHash as createHash8 } from "node:crypto";
96460
+ import { extname as extname3, join as join28 } from "node:path";
96461
+ function contentFileName(key, name2) {
96462
+ const extension = extname3(name2).toLowerCase();
96463
+ return /^\.[a-z0-9]{1,16}$/u.test(extension) ? `${key}${extension}` : key;
96464
+ }
96373
96465
  var contentRequest, CONTENT_LIFETIME_MILLISECONDS, metadataSchema, LocalExternalContentStore;
96374
96466
  var init_external_content_store = __esm({
96375
96467
  "src/engine/session/content/external-content-store.ts"() {
@@ -96425,7 +96517,7 @@ var init_external_content_store = __esm({
96425
96517
  const active = AbortSignal.any([signal, this.#lifetime.signal]);
96426
96518
  active.throwIfAborted();
96427
96519
  const metadata = metadataSchema.parse({ kind, mediaType, name: name2 });
96428
- const key = createHash7("sha256").update(bytes).digest("hex");
96520
+ const key = createHash8("sha256").update(bytes).digest("hex");
96429
96521
  const request = `mobius-content:${kind}:${key}`;
96430
96522
  const existing = this.#entries.get(request);
96431
96523
  if (existing !== void 0 && existing.expiresAt > Date.now()) {
@@ -96438,7 +96530,7 @@ var init_external_content_store = __esm({
96438
96530
  }
96439
96531
  await this.#expire();
96440
96532
  active.throwIfAborted();
96441
- const path8 = await this.#scratch.write(kind, key, bytes, active);
96533
+ const path8 = await this.#scratch.write(kind, contentFileName(key, metadata.name), bytes, active);
96442
96534
  active.throwIfAborted();
96443
96535
  const retained = this.#entries.get(request);
96444
96536
  this.#entries.set(request, {
@@ -96474,7 +96566,11 @@ var init_external_content_store = __esm({
96474
96566
  read = (sessionId, request, signal) => this.#track(async () => {
96475
96567
  const active = AbortSignal.any([signal, this.#lifetime.signal]);
96476
96568
  const resolved = await this.#resolve(sessionId, request, active);
96477
- return resolved === null ? null : this.#scratch.read(resolved.path, active);
96569
+ if (resolved === null) {
96570
+ return null;
96571
+ }
96572
+ const bytes = await this.#scratch.read(resolved.path, active);
96573
+ return bytes === null ? null : { ...resolved, bytes };
96478
96574
  });
96479
96575
  [Symbol.asyncDispose] = async () => {
96480
96576
  clearInterval(this.#expirationTimer);
@@ -97445,7 +97541,7 @@ var init_agent_settings_adapter = __esm({
97445
97541
  });
97446
97542
 
97447
97543
  // src/engine/session/settings/discover-agent-settings.ts
97448
- import { createHash as createHash8 } from "node:crypto";
97544
+ import { createHash as createHash9 } from "node:crypto";
97449
97545
  function agentSettingsFailureDetails(agentId, failure2) {
97450
97546
  const details = { agentId, code: operationErrorCode(failure2) };
97451
97547
  return failure2 instanceof AcpConfigurationValueUnavailableError ? {
@@ -97710,7 +97806,7 @@ function settingsRevision(agent, reportedAgentVersion, settings, modelEffort) {
97710
97806
  unsupportedModelIds: modelEffort.unsupportedModelIds,
97711
97807
  valuesByModel: {}
97712
97808
  };
97713
- return createHash8("sha256").update(
97809
+ return createHash9("sha256").update(
97714
97810
  JSON.stringify({
97715
97811
  adapterVersion: agent.adapterVersion,
97716
97812
  agentId: agent.id,
@@ -97893,7 +97989,7 @@ var init_agent_settings = __esm({
97893
97989
  });
97894
97990
 
97895
97991
  // src/engine/runtime/command/registry/content-write/index.ts
97896
- import { createHash as createHash9 } from "node:crypto";
97992
+ import { createHash as createHash10 } from "node:crypto";
97897
97993
  function createContentWriteCommands(context) {
97898
97994
  const pending = /* @__PURE__ */ new Map();
97899
97995
  const operations = /* @__PURE__ */ new Set();
@@ -97923,7 +98019,7 @@ function createContentWriteCommands(context) {
97923
98019
  pending.set(message.requestId, {
97924
98020
  bytes: new Uint8Array(message.byteLength),
97925
98021
  command: message,
97926
- hash: createHash9("sha256"),
98022
+ hash: createHash10("sha256"),
97927
98023
  receivedBytes: 0,
97928
98024
  sequence: 0
97929
98025
  });
@@ -98021,7 +98117,7 @@ var init_content_write = __esm({
98021
98117
  });
98022
98118
 
98023
98119
  // src/engine/session/content/session-files.ts
98024
- import { basename as basename9, extname as extname3, isAbsolute as isAbsolute16, join as join32, normalize as normalize3, resolve as resolve17 } from "node:path";
98120
+ import { basename as basename9, extname as extname4, isAbsolute as isAbsolute16, join as join32, normalize as normalize3, resolve as resolve17 } from "node:path";
98025
98121
  import { fileURLToPath as fileURLToPath2 } from "node:url";
98026
98122
  function requestedPath(value) {
98027
98123
  if (value.includes("\0")) {
@@ -98077,7 +98173,7 @@ async function streamLocalFile(command, send2, sendChunk, signal, workingDirecto
98077
98173
  send2({
98078
98174
  byteLength: before.size,
98079
98175
  kind: FILE_BEGIN_KIND,
98080
- mediaType: external?.mediaType ?? raster?.mimeType ?? (extname3(target).toLowerCase() === ".svg" ? "image/svg+xml" : "application/octet-stream"),
98176
+ mediaType: external?.mediaType ?? raster?.mimeType ?? (extname4(target).toLowerCase() === ".svg" ? "image/svg+xml" : "application/octet-stream"),
98081
98177
  modifiedAt: before.modifiedAt.toISOString(),
98082
98178
  name: external?.name ?? (basename9(target) || "file"),
98083
98179
  requestId: command.requestId,
@@ -98860,75 +98956,6 @@ var init_apply_session_configuration = __esm({
98860
98956
  }
98861
98957
  });
98862
98958
 
98863
- // src/engine/session/prompt/prompt-artifacts.ts
98864
- import { createHash as createHash10 } from "node:crypto";
98865
- function isTextMediaType(mediaType) {
98866
- return mediaType.startsWith("text/") || mediaType === "application/json" || mediaType.endsWith("+json") || mediaType === "application/xml" || mediaType.endsWith("+xml");
98867
- }
98868
- async function resolvePromptArtifacts(read, attachments, signal) {
98869
- const images = [];
98870
- const resources = [];
98871
- for (const attachment of attachments) {
98872
- signal.throwIfAborted();
98873
- const bytes = await read(attachment, signal);
98874
- if (bytes.byteLength !== attachment.byteLength || createHash10("sha256").update(bytes).digest("hex") !== attachment.contentKey) {
98875
- throw new PromptArtifactError(
98876
- "artifact_integrity_failed",
98877
- "The prompt attachment failed integrity validation"
98878
- );
98879
- }
98880
- if (attachment.kind === "image") {
98881
- if (bytes.byteLength > PROMPT_IMAGE_MAX_BYTES || images.length >= 8) {
98882
- continue;
98883
- }
98884
- images.push({
98885
- data: Buffer.from(bytes).toString("base64"),
98886
- mimeType: attachment.mimeType,
98887
- name: attachment.name
98888
- });
98889
- continue;
98890
- }
98891
- if (!isTextMediaType(attachment.mimeType)) {
98892
- continue;
98893
- }
98894
- if (bytes.byteLength > MAX_AGENT_RESOURCE_BYTE_LENGTH || resources.length >= 16) {
98895
- continue;
98896
- }
98897
- let text2;
98898
- try {
98899
- text2 = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
98900
- } catch {
98901
- signal.throwIfAborted();
98902
- continue;
98903
- }
98904
- resources.push({ mimeType: attachment.mimeType, text: text2, type: "embedded", uri: attachment.uri });
98905
- }
98906
- return new ResolvedPromptArtifacts(images, resources);
98907
- }
98908
- var MAX_AGENT_RESOURCE_BYTE_LENGTH, PromptArtifactError, ResolvedPromptArtifacts;
98909
- var init_prompt_artifacts = __esm({
98910
- "src/engine/session/prompt/prompt-artifacts.ts"() {
98911
- "use strict";
98912
- init_protocol();
98913
- init_operation_error();
98914
- MAX_AGENT_RESOURCE_BYTE_LENGTH = 1e6;
98915
- PromptArtifactError = class extends OperationError {
98916
- constructor(code, message, options = {}) {
98917
- super(code, message, options);
98918
- this.name = "PromptArtifactError";
98919
- }
98920
- };
98921
- ResolvedPromptArtifacts = class {
98922
- images;
98923
- resources;
98924
- constructor(images, resources) {
98925
- this.images = images;
98926
- this.resources = resources;
98927
- }
98928
- };
98929
- }
98930
- });
98931
-
98932
98959
  // src/engine/session/prompt/execute-prompt.ts
98933
98960
  async function executePrompt({
98934
98961
  operations,
@@ -99158,16 +99185,16 @@ var init_prompt2 = __esm({
99158
99185
  eventProjection: context.eventProjection,
99159
99186
  command: message,
99160
99187
  resolveAgent: context.resolveAgent,
99161
- readArtifact: async (artifact, signal) => {
99162
- const bytes = await context.externalContent.read(
99188
+ readArtifact: async (attachment, signal) => {
99189
+ const artifact = await context.externalContent.read(
99163
99190
  message.sessionId,
99164
- `mobius-content:${artifact.kind}:${artifact.contentKey}`,
99191
+ `mobius-content:${attachment.kind}:${attachment.contentKey}`,
99165
99192
  signal
99166
99193
  );
99167
- if (bytes === null) {
99194
+ if (artifact === null) {
99168
99195
  throw new Error("The prompt attachment is unavailable");
99169
99196
  }
99170
- return bytes;
99197
+ return { bytes: artifact.bytes, path: artifact.path };
99171
99198
  },
99172
99199
  reportFailure: context.diagnostics.daemonDiagnostic,
99173
99200
  directories: context.directories,
@@ -99292,7 +99319,12 @@ async function prepareSession(command, recovery, closed, config2, resolveAgent,
99292
99319
  }
99293
99320
  async function executeWorkspaceList(socket, command, directories, signal) {
99294
99321
  try {
99295
- const listing = await directories.list(command.bindingId, command.directoryId, signal);
99322
+ const listing = command.view === "metadata" ? await directories.describe(command.bindingId, command.directoryId, signal).then((directory) => ({
99323
+ directoryId: command.directoryId,
99324
+ directoryLabel: directory.label,
99325
+ entries: [],
99326
+ parentDirectoryId: directory.parentDirectoryId
99327
+ })) : await directories.list(command.bindingId, command.directoryId, signal);
99296
99328
  send(socket, {
99297
99329
  bindingId: command.bindingId,
99298
99330
  directoryId: listing.directoryId,
@@ -100296,13 +100328,23 @@ var init_session_event_publisher = __esm({
100296
100328
  return;
100297
100329
  }
100298
100330
  if (batch === void 0 || newBatch) {
100299
- batch = { events: [], bytes: EMPTY_ENVELOPE_BYTES };
100331
+ batch = { events: [], firstAnswers: [], bytes: EMPTY_ENVELOPE_BYTES };
100300
100332
  this.#pending.push(batch);
100301
100333
  }
100302
100334
  batch.events.push(event);
100303
100335
  batch.bytes += bytes;
100304
100336
  this.#pendingBytes += additionalBytes;
100337
+ const awaiting = this.#awaitingFirstAnswer.get(event.sessionId);
100305
100338
  const firstAnswer = this.#consumeFirstAnswer(event);
100339
+ if (firstAnswer && awaiting !== void 0) {
100340
+ batch.firstAnswers.push({
100341
+ sessionId: event.sessionId,
100342
+ commandId: awaiting.commandId,
100343
+ eventId: event.eventId,
100344
+ observedAt: Date.now(),
100345
+ publishedAt: performance.now()
100346
+ });
100347
+ }
100306
100348
  const connection = this.#connection;
100307
100349
  if (connection.kind !== "connected" || connection.socket.readyState !== WebSocket9.OPEN) {
100308
100350
  return;
@@ -100357,6 +100399,19 @@ var init_session_event_publisher = __esm({
100357
100399
  this.#pendingBytes -= batch.bytes;
100358
100400
  try {
100359
100401
  socket.send(encoded);
100402
+ for (const answer of batch.firstAnswers) {
100403
+ console.info(
100404
+ JSON.stringify({
100405
+ event: "runtime-first-answer-handoff-timing",
100406
+ sessionId: answer.sessionId,
100407
+ commandId: answer.commandId,
100408
+ eventId: answer.eventId,
100409
+ observedAt: answer.observedAt,
100410
+ handedOffAt: Date.now(),
100411
+ publishToHandoffMs: performance.now() - answer.publishedAt
100412
+ })
100413
+ );
100414
+ }
100360
100415
  } catch {
100361
100416
  console.warn("Live Session output could not be sent", { code: "live_event_send_failed" });
100362
100417
  break;