@omercnet/paseo-omp 0.4.0-next.119.1 → 0.4.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.
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { constants, type Dir } from "node:fs";
3
3
  import { type FileHandle, lstat, open, opendir, realpath } from "node:fs/promises";
4
4
  import { homedir } from "node:os";
5
- import { basename, isAbsolute, join, relative, resolve } from "node:path";
5
+ import { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
6
6
  import { ompSessionDir } from "../paths";
7
7
  import { isValidImagePayload } from "./image";
8
8
 
@@ -510,19 +510,16 @@ interface PersistedTranscriptNode {
510
510
  message?: unknown;
511
511
  }
512
512
 
513
- function persistedTranscriptMessage(record: Record<string, unknown>, entryId?: string): unknown {
513
+ function persistedTranscriptMessage(record: Record<string, unknown>, entryId: string): unknown {
514
514
  if (record.type === "message") {
515
515
  if (!record.message || typeof record.message !== "object" || Array.isArray(record.message))
516
516
  return;
517
- return {
518
- ...(record.message as Record<string, unknown>),
519
- ...(entryId ? { entryId } : {}),
520
- };
517
+ return { ...(record.message as Record<string, unknown>), entryId };
521
518
  }
522
519
  if (record.type !== "custom_message") return;
523
520
  return {
524
521
  role: "custom",
525
- ...(entryId ? { entryId } : {}),
522
+ entryId,
526
523
  customType: record.customType,
527
524
  content: record.content,
528
525
  display: record.display,
@@ -683,7 +680,6 @@ export async function readOmpPersistedSubagentTranscript(
683
680
  childTranscriptId: string,
684
681
  cwd: string,
685
682
  signal?: AbortSignal,
686
- childSessionFile?: string,
687
683
  ): Promise<OmpPersistedSubagentTranscript> {
688
684
  signal?.throwIfAborted();
689
685
  if (
@@ -691,12 +687,7 @@ export async function readOmpPersistedSubagentTranscript(
691
687
  !parentSessionFile.endsWith(".jsonl") ||
692
688
  parentSessionFile.includes("\0") ||
693
689
  !CHILD_TRANSCRIPT_ID.test(childTranscriptId) ||
694
- basename(childTranscriptId) !== childTranscriptId ||
695
- (childSessionFile !== undefined &&
696
- (!isAbsolute(childSessionFile) ||
697
- !childSessionFile.endsWith(".jsonl") ||
698
- childSessionFile.includes("\0") ||
699
- basename(childSessionFile) !== `${childTranscriptId}.jsonl`))
690
+ basename(childTranscriptId) !== childTranscriptId
700
691
  ) {
701
692
  throw new Error("Invalid OMP child transcript descriptor");
702
693
  }
@@ -716,53 +707,34 @@ export async function readOmpPersistedSubagentTranscript(
716
707
  await parentHandle.close().catch(() => undefined);
717
708
  }
718
709
  const canonicalParent = await realpath(parentSessionFile);
719
- const expectedDirectory = canonicalParent.slice(0, -".jsonl".length);
710
+ const parentExtension = extname(canonicalParent);
711
+ const expectedDirectory = canonicalParent.slice(0, -parentExtension.length);
720
712
  const canonicalDirectory = await realpath(expectedDirectory).catch(() => undefined);
721
713
  if (!canonicalDirectory || canonicalDirectory !== expectedDirectory) {
722
714
  throw new Error("OMP child transcript directory is not canonically owned by its parent");
723
715
  }
724
- const requestedFile = resolve(
725
- childSessionFile ?? join(canonicalDirectory, `${childTranscriptId}.jsonl`),
726
- );
716
+ const sessionFile = join(canonicalDirectory, `${childTranscriptId}.jsonl`);
727
717
  let handle: FileHandle;
728
718
  try {
729
719
  handle = await open(
730
- requestedFile,
720
+ sessionFile,
731
721
  constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0) | (constants.O_NONBLOCK ?? 0),
732
722
  );
733
723
  } catch {
734
724
  throw new Error("OMP child transcript could not be opened");
735
725
  }
736
726
  try {
737
- const [stat, pathStat, canonicalChild] = await Promise.all([
738
- handle.stat(),
739
- lstat(requestedFile),
740
- realpath(requestedFile),
741
- ]);
727
+ const [stat, canonicalChild] = await Promise.all([handle.stat(), realpath(sessionFile)]);
742
728
  if (
743
729
  !stat.isFile() ||
744
- !pathStat.isFile() ||
745
- pathStat.isSymbolicLink() ||
746
- stat.dev !== pathStat.dev ||
747
- stat.ino !== pathStat.ino ||
748
- stat.size > MAX_CHILD_TRANSCRIPT_BYTES
749
- ) {
750
- throw new Error("OMP child transcript failed ownership validation");
751
- }
752
- const canonicalRelativeChild = relative(canonicalDirectory, canonicalChild);
753
- if (
754
- !canonicalRelativeChild ||
755
- canonicalRelativeChild.startsWith("..") ||
756
- isAbsolute(canonicalRelativeChild)
730
+ stat.size > MAX_CHILD_TRANSCRIPT_BYTES ||
731
+ dirname(canonicalChild) !== canonicalDirectory ||
732
+ canonicalChild !== sessionFile
757
733
  ) {
758
734
  throw new Error("OMP child transcript failed ownership validation");
759
735
  }
760
- const bytes = await readStableFile(
761
- handle,
762
- stat.size,
763
- signal,
764
- "OMP child transcript changed while reading",
765
- );
736
+ const bytes = await handle.readFile();
737
+ signal?.throwIfAborted();
766
738
  const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
767
739
  const messages: unknown[] = [];
768
740
  let nativeSessionId: string | undefined;
@@ -780,21 +752,15 @@ export async function readOmpPersistedSubagentTranscript(
780
752
  nativeSessionId ??= candidateId;
781
753
  if (nativeSessionId !== candidateId)
782
754
  throw new Error("OMP child transcript identity changed");
783
- } else {
784
- const entryId =
785
- typeof record.id === "string" && CHILD_TRANSCRIPT_ID.test(record.id)
786
- ? record.id
787
- : undefined;
788
- const message = persistedTranscriptMessage(record, entryId);
789
- if (message === undefined) continue;
755
+ } else if (record.type === "message" && record.message !== undefined) {
790
756
  if (messages.length >= MAX_CHILD_TRANSCRIPT_MESSAGES) {
791
757
  throw new Error("OMP child transcript exceeds message limits");
792
758
  }
793
- messages.push(message);
759
+ messages.push(record.message);
794
760
  }
795
761
  }
796
762
  if (!nativeSessionId) throw new Error("OMP child transcript is missing session identity");
797
- return { sessionFile: canonicalChild, nativeSessionId, byteLength: bytes.byteLength, messages };
763
+ return { sessionFile, nativeSessionId, byteLength: bytes.byteLength, messages };
798
764
  } catch (error) {
799
765
  if (error instanceof Error) throw error;
800
766
  throw new Error("OMP child transcript could not be decoded");
@@ -1685,7 +1685,7 @@ export class OmpProviderSession {
1685
1685
  });
1686
1686
  }
1687
1687
  }
1688
- messages ??= await waitForReplay(this.runtime.getMessages(replay.signal), replay.signal);
1688
+ messages ??= await waitForReplay(this.runtime.getMessages(), replay.signal);
1689
1689
  this.quarantineBranchEntries();
1690
1690
  for (const message of messages) {
1691
1691
  replay.signal.throwIfAborted();
@@ -3234,17 +3234,6 @@ export class OmpProviderSession {
3234
3234
  this.handleRuntimeFailure();
3235
3235
  return;
3236
3236
  }
3237
- if (
3238
- event.type === "config_warnings_changed" ||
3239
- event.type === "advisor_cost_changed" ||
3240
- event.type === "ttsr_triggered"
3241
- ) {
3242
- return;
3243
- }
3244
- if (event.type === "irc_message") {
3245
- this.projector.projectPassive(event);
3246
- return;
3247
- }
3248
3237
  if (
3249
3238
  event.type === "notice" ||
3250
3239
  event.type === "todo_reminder" ||
@@ -1,5 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
- import { basename, extname, isAbsolute } from "node:path";
2
+ import { basename, extname } from "node:path";
3
3
  import type { ProviderEvent } from "@getpaseo/plugin/server/provider";
4
4
  import { z } from "zod";
5
5
  import type {
@@ -882,32 +882,10 @@ export class OmpSubsessionProjector {
882
882
  signal.throwIfAborted();
883
883
  if (this.sessionIdByNativeId.has(snapshot.id)) continue;
884
884
  try {
885
- let history: ReplayHistory;
886
- if (snapshot.sessionFile && isAbsolute(snapshot.sessionFile) && this.rootSessionFile) {
887
- try {
888
- history = await waitForReplay(
889
- runtime.readPersistedSubagentTranscript({
890
- parentSessionFile: this.rootSessionFile,
891
- childTranscriptId: snapshot.id,
892
- sessionFile: snapshot.sessionFile,
893
- cwd: this.cwd,
894
- signal,
895
- }),
896
- signal,
897
- );
898
- } catch (error) {
899
- if (signal.aborted) throw error;
900
- history = await waitForReplay(
901
- runtimeSession.getSubagentMessages({ subagentId: snapshot.id }),
902
- signal,
903
- );
904
- }
905
- } else {
906
- history = await waitForReplay(
907
- runtimeSession.getSubagentMessages({ subagentId: snapshot.id }),
908
- signal,
909
- );
910
- }
885
+ const history = await waitForReplay(
886
+ runtimeSession.getSubagentMessages({ subagentId: snapshot.id }),
887
+ signal,
888
+ );
911
889
  try {
912
890
  this.accountReplay(history.messages, budget, signal);
913
891
  collected.push({
@@ -921,7 +899,7 @@ export class OmpSubsessionProjector {
921
899
  }
922
900
  } catch (error) {
923
901
  if (signal.aborted) throw error;
924
- collected.push({ snapshot, sessionFile: snapshot.sessionFile });
902
+ collected.push({ snapshot });
925
903
  }
926
904
  }
927
905
  collected.sort((left, right) => {
@@ -6,12 +6,7 @@ import type {
6
6
  } from "@getpaseo/plugin/server/provider";
7
7
  import { OMP_MCP_AUTH_TIMELINE_KIND } from "../../shared/mcp";
8
8
  import { isOmpImageMimeType, isValidImagePayload, type OmpImageMimeType } from "./image";
9
- import {
10
- OMP_MAX_CONTENT_PARTS,
11
- OMP_MESSAGE_REPLAY_POLICIES,
12
- type OmpMessage,
13
- type OmpRpcEvent,
14
- } from "./omp-rpc";
9
+ import { OMP_MAX_CONTENT_PARTS, type OmpMessage, type OmpRpcEvent } from "./omp-rpc";
15
10
  import {
16
11
  boundedJsonBytes,
17
12
  type JsonValue,
@@ -139,10 +134,6 @@ function imageBlock(data: string, mimeType: string): StreamBlockSnapshot | undef
139
134
  }
140
135
 
141
136
  type OmpAssistantMessage = Extract<OmpMessage, { role: "assistant" }>;
142
- type OmpCustomDisplayMessage = Extract<
143
- OmpMessage,
144
- { role: "bashExecution" | "pythonExecution" | "custom" | "hookMessage" }
145
- >;
146
137
 
147
138
  function blockText(
148
139
  message: OmpAssistantMessage,
@@ -426,35 +417,16 @@ export class OmpTimelineProjector {
426
417
  return;
427
418
  }
428
419
  }
429
- if (
430
- (event.type === "message_start" ||
431
- event.type === "message_update" ||
432
- event.type === "message_end") &&
433
- OMP_MESSAGE_REPLAY_POLICIES[event.message.role] === "ignore"
434
- ) {
435
- return;
436
- }
437
420
  if (
438
421
  event.type === "todo_reminder" ||
439
422
  event.type === "todo_auto_clear" ||
440
- event.type === "goal_updated" ||
441
- event.type === "auto_retry_start" ||
442
- event.type === "auto_retry_end" ||
443
- event.type === "retry_fallback_applied" ||
444
- event.type === "retry_fallback_succeeded" ||
445
423
  event.type === "notice" ||
446
424
  event.type === "extension_ui_request" ||
447
425
  event.type === "auto_compaction_start" ||
448
426
  event.type === "auto_compaction_end" ||
449
427
  event.type === "compaction_start" ||
450
428
  event.type === "compaction_end" ||
451
- event.type === "advisor_yielded" ||
452
- event.type === "config_warnings_changed" ||
453
- event.type === "advisor_cost_changed" ||
454
- event.type === "ttsr_triggered" ||
455
- event.type === "irc_message" ||
456
- event.type === "model_changed" ||
457
- event.type === "thinking_level_changed"
429
+ event.type === "advisor_yielded"
458
430
  ) {
459
431
  this.projectPassive(event);
460
432
  return;
@@ -477,12 +449,7 @@ export class OmpTimelineProjector {
477
449
  this.scheduleFlush();
478
450
  return;
479
451
  case "message_end":
480
- if (
481
- event.message.role === "custom" ||
482
- event.message.role === "hookMessage" ||
483
- event.message.role === "bashExecution" ||
484
- event.message.role === "pythonExecution"
485
- ) {
452
+ if (event.message.role === "custom" || event.message.role === "bashExecution") {
486
453
  this.publishCustomMessage(event.message);
487
454
  return;
488
455
  }
@@ -532,13 +499,12 @@ export class OmpTimelineProjector {
532
499
  if (!snapshot.silent) this.publishTool(snapshot, "running");
533
500
  return;
534
501
  }
535
- case "tool_execution_update":
536
- case "tool_stream_update": {
502
+ case "tool_execution_update": {
537
503
  const previous = this.tools.get(event.toolCallId);
538
504
  if (!previous) return;
539
505
  if (previous.turnId !== turnId || previous.generation !== this.runtimeGeneration) return;
540
506
  const output = this.dataFilter.json(
541
- event.type === "tool_execution_update" ? event.partialResult : event.update,
507
+ event.partialResult,
542
508
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
543
509
  MAX_PUBLIC_TOOL_PAYLOAD_BYTES,
544
510
  );
@@ -634,10 +600,6 @@ export class OmpTimelineProjector {
634
600
  this.publishRetryFallback(event);
635
601
  return;
636
602
  }
637
- if (event.type === "irc_message") {
638
- this.publishCustomMessage(event.message as OmpCustomDisplayMessage);
639
- return;
640
- }
641
603
  if (event.type === "notice") {
642
604
  this.noticeSequence += 1;
643
605
  this.publish({
@@ -877,9 +839,8 @@ export class OmpTimelineProjector {
877
839
 
878
840
  projectReplayMessage(message: OmpMessage): void {
879
841
  if (this.closed) return;
842
+ const nativeIdentity = assistantIdentity(message);
880
843
  this.replaySequence += 1;
881
- if (OMP_MESSAGE_REPLAY_POLICIES[message.role] === "ignore") return;
882
-
883
844
  if (message.role === "user") {
884
845
  this.replayToolCalls.clear();
885
846
  if (this.replayTurnId) this.finishTurn(this.replayTurnId);
@@ -900,9 +861,7 @@ export class OmpTimelineProjector {
900
861
  }
901
862
  return;
902
863
  }
903
-
904
864
  if (message.role === "assistant") {
905
- const nativeIdentity = assistantIdentity(message);
906
865
  this.replayTurnId ??= `omp:replay-turn:${this.replaySequence}`;
907
866
  this.projectingReplay = true;
908
867
  try {
@@ -921,7 +880,6 @@ export class OmpTimelineProjector {
921
880
  if (nativeIdentity) this.rememberReplayOccurrence(nativeIdentity, message);
922
881
  return;
923
882
  }
924
-
925
883
  this.replayTurnId ??= `omp:replay-turn:${this.replaySequence}`;
926
884
  if (message.role === "toolResult") {
927
885
  const replayCall = this.replayToolCalls.get(message.toolCallId);
@@ -952,18 +910,10 @@ export class OmpTimelineProjector {
952
910
  );
953
911
  return;
954
912
  }
955
-
956
- if (
957
- message.role === "bashExecution" ||
958
- message.role === "pythonExecution" ||
959
- message.role === "custom" ||
960
- message.role === "hookMessage"
961
- ) {
913
+ if (message.role === "bashExecution") {
962
914
  this.publishCustomMessage(message);
963
- if (message.role === "bashExecution" || message.role === "pythonExecution") {
964
- this.finishTurn(this.replayTurnId);
965
- this.replayTurnId = null;
966
- }
915
+ this.finishTurn(this.replayTurnId);
916
+ this.replayTurnId = null;
967
917
  }
968
918
  }
969
919
 
@@ -1400,17 +1350,9 @@ export class OmpTimelineProjector {
1400
1350
  return undefined;
1401
1351
  }
1402
1352
 
1403
- private publishCustomMessage(message: OmpCustomDisplayMessage): void {
1404
- if (
1405
- (message.role === "custom" || message.role === "hookMessage") &&
1406
- message.display === false
1407
- ) {
1408
- return;
1409
- }
1410
- const rawType =
1411
- message.role === "custom" || message.role === "hookMessage"
1412
- ? (message.customType ?? message.role)
1413
- : message.role;
1353
+ private publishCustomMessage(message: OmpMessage): void {
1354
+ if (message.display === false) return;
1355
+ const rawType = message.customType ?? message.role;
1414
1356
  const publicType = this.dataFilter.text(rawType, 256);
1415
1357
  const lowerType = rawType.toLowerCase();
1416
1358
  const details = jsonRecord(this.dataFilter.json(message.details ?? null));
@@ -1419,9 +1361,7 @@ export class OmpTimelineProjector {
1419
1361
  const id = nativeIdentity
1420
1362
  ? `omp:custom:${createHash("sha256").update(nativeIdentity).digest("base64url").slice(0, 12)}`
1421
1363
  : `omp:custom:${this.customSequence}`;
1422
- const messageContent =
1423
- message.role === "custom" || message.role === "hookMessage" ? message.content : undefined;
1424
- const contentParts = Array.isArray(messageContent) ? messageContent : [];
1364
+ const contentParts = Array.isArray(message.content) ? message.content : [];
1425
1365
  const imageResult = nativeImageResult(
1426
1366
  {
1427
1367
  content:
@@ -1433,26 +1373,16 @@ export class OmpTimelineProjector {
1433
1373
  this.dataFilter,
1434
1374
  );
1435
1375
  const content =
1436
- typeof messageContent === "string"
1437
- ? messageContent
1376
+ typeof message.content === "string"
1377
+ ? message.content
1438
1378
  : contentParts
1439
1379
  .flatMap((part) => (part.type === "text" && part.text ? [part.text] : []))
1440
1380
  .join("\n\n");
1441
- const execution = message.role === "bashExecution" || message.role === "pythonExecution";
1442
- if (execution || /bash|shell|python/u.test(lowerType)) {
1443
- const nativeCommand =
1444
- message.role === "bashExecution"
1445
- ? message.command
1446
- : message.role === "pythonExecution"
1447
- ? message.code
1448
- : undefined;
1381
+ if (message.role === "bashExecution" || /bash|shell|python/u.test(lowerType)) {
1449
1382
  const command = this.dataFilter.text(
1450
- nativeCommand ?? firstString(details, "command", "input") ?? publicType,
1383
+ message.command ?? firstString(details, "command", "input") ?? publicType,
1451
1384
  );
1452
- const output = execution ? (message.output ?? content) : content;
1453
- const exitCode = execution ? message.exitCode : undefined;
1454
- const cancelled = execution ? message.cancelled : undefined;
1455
- const truncated = execution ? message.truncated : undefined;
1385
+ const output = message.output ?? content;
1456
1386
  this.publish({
1457
1387
  type: "tool_call",
1458
1388
  id,
@@ -1463,21 +1393,21 @@ export class OmpTimelineProjector {
1463
1393
  command,
1464
1394
  ...(firstString(details, "cwd") ? { cwd: firstString(details, "cwd") } : {}),
1465
1395
  ...(output ? { output: this.dataFilter.text(output, MAX_STREAM_TEXT_LENGTH) } : {}),
1466
- ...(typeof exitCode === "number" || exitCode === null
1467
- ? { exitCode }
1396
+ ...(typeof message.exitCode === "number" || message.exitCode === null
1397
+ ? { exitCode: message.exitCode }
1468
1398
  : typeof details?.exitCode === "number"
1469
1399
  ? { exitCode: details.exitCode }
1470
1400
  : {}),
1471
1401
  },
1472
- ...(cancelled !== undefined || truncated !== undefined
1402
+ ...(message.cancelled !== undefined || message.truncated !== undefined
1473
1403
  ? {
1474
1404
  metadata: {
1475
- ...(cancelled !== undefined ? { cancelled } : {}),
1476
- ...(truncated !== undefined ? { truncated } : {}),
1405
+ ...(message.cancelled !== undefined ? { cancelled: message.cancelled } : {}),
1406
+ ...(message.truncated !== undefined ? { truncated: message.truncated } : {}),
1477
1407
  },
1478
1408
  }
1479
1409
  : {}),
1480
- status: cancelled ? "canceled" : "completed",
1410
+ status: message.cancelled ? "canceled" : "completed",
1481
1411
  error: null,
1482
1412
  });
1483
1413
  if (imageResult) this.publishImages(id, publicType, imageResult.image);