@sentry/junior 0.96.0 → 0.97.1

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 (135) hide show
  1. package/dist/{agent-hooks-5URSMY3O.js → agent-hooks-XSUIVRQS.js} +8 -6
  2. package/dist/{reporting → api}/conversations/activity.d.ts +1 -1
  3. package/dist/api/conversations/detail-projection.d.ts +10 -0
  4. package/dist/api/conversations/detail.d.ts +3 -0
  5. package/dist/api/conversations/detail.query.d.ts +3 -0
  6. package/dist/api/conversations/list.d.ts +3 -0
  7. package/dist/api/conversations/list.query.d.ts +94 -0
  8. package/dist/api/conversations/projection.d.ts +14 -0
  9. package/dist/api/conversations/schema.d.ts +683 -0
  10. package/dist/{reporting → api}/conversations/shared.d.ts +1 -5
  11. package/dist/api/conversations/stats.d.ts +3 -0
  12. package/dist/api/conversations/stats.query.d.ts +3 -0
  13. package/dist/api/conversations/subagent.d.ts +3 -0
  14. package/dist/{reporting → api}/conversations/transcript.d.ts +4 -4
  15. package/dist/api/people/list.d.ts +1 -2
  16. package/dist/api/people/list.query.d.ts +3 -4
  17. package/dist/api/people/profile.d.ts +1 -2
  18. package/dist/api/people/profile.query.d.ts +3 -4
  19. package/dist/api/people/schema.d.ts +193 -0
  20. package/dist/api/people/shared.d.ts +31 -9
  21. package/dist/api/schema.d.ts +20 -0
  22. package/dist/api/schema.js +50 -0
  23. package/dist/api-reference.d.ts +4 -3
  24. package/dist/api.d.ts +3 -0
  25. package/dist/api.js +156 -0
  26. package/dist/app.d.ts +1 -4
  27. package/dist/app.js +101 -367
  28. package/dist/build/virtual-config.d.ts +2 -2
  29. package/dist/{catalog-runtime-MSMAYYIK.js → catalog-runtime-PFHVMHDR.js} +3 -2
  30. package/dist/chat/agent/resume.d.ts +3 -1
  31. package/dist/chat/agent/tools.d.ts +2 -1
  32. package/dist/chat/config.d.ts +1 -8
  33. package/dist/chat/conversations/history.d.ts +241 -27
  34. package/dist/chat/conversations/legacy-advisor-session.d.ts +7 -0
  35. package/dist/chat/conversations/legacy-import.d.ts +2 -2
  36. package/dist/chat/conversations/projection.d.ts +19 -3
  37. package/dist/chat/conversations/sql/legacy-history-import.d.ts +1 -1
  38. package/dist/chat/conversations/sql/store.d.ts +15 -9
  39. package/dist/chat/conversations/store.d.ts +9 -4
  40. package/dist/chat/model-profile.d.ts +14 -0
  41. package/dist/chat/pi/transcript.d.ts +5 -3
  42. package/dist/chat/resource-events/store.d.ts +2 -2
  43. package/dist/chat/services/context-compaction.d.ts +15 -0
  44. package/dist/chat/services/turn-result.d.ts +1 -0
  45. package/dist/chat/services/turn-session-record.d.ts +6 -6
  46. package/dist/chat/state/turn-session.d.ts +16 -5
  47. package/dist/chat/task-execution/store.d.ts +2 -2
  48. package/dist/chat/tools/handoff/tool.d.ts +45 -0
  49. package/dist/chat/tools/types.d.ts +7 -2
  50. package/dist/chat/usage.d.ts +25 -25
  51. package/dist/{chunk-RITD7E5L.js → chunk-35BNLDOJ.js} +338 -577
  52. package/dist/{chunk-PMNN6ST4.js → chunk-3ECW2K2O.js} +7 -2
  53. package/dist/{chunk-5C6XTTFI.js → chunk-3F4OPKGF.js} +89 -55
  54. package/dist/chunk-3V7OFJZC.js +264 -0
  55. package/dist/chunk-4ZNGQH7C.js +25 -0
  56. package/dist/{chunk-S5KZ5LPG.js → chunk-5HLDPVYS.js} +14 -447
  57. package/dist/{chunk-DZENG5ML.js → chunk-6U26PYI5.js} +24 -14
  58. package/dist/chunk-AUUCAFDE.js +546 -0
  59. package/dist/{chunk-6APU57E6.js → chunk-B3IT5END.js} +41 -260
  60. package/dist/chunk-CEA3A3UA.js +86 -0
  61. package/dist/{chunk-YDT7SNCN.js → chunk-D5KZYS4T.js} +1 -1
  62. package/dist/{chunk-YDVEH3SB.js → chunk-EEZCY5WX.js} +78 -28
  63. package/dist/{chunk-2NAZZPPS.js → chunk-EOWXRY5E.js} +1 -1
  64. package/dist/{chunk-GQ3GK22G.js → chunk-FBHZJFHI.js} +2 -2
  65. package/dist/{chunk-CZEX6KIQ.js → chunk-G3E7SCME.js} +0 -23
  66. package/dist/chunk-GUAP3H7O.js +319 -0
  67. package/dist/{chunk-BYZWPRJA.js → chunk-H66UCBPW.js} +6 -4
  68. package/dist/{chunk-L7CDEIVJ.js → chunk-LMJPRYHP.js} +4 -2
  69. package/dist/chunk-LUAE4MGW.js +199 -0
  70. package/dist/{chunk-GY34VGP5.js → chunk-LVA4I4H7.js} +3 -1
  71. package/dist/chunk-LVUKF5CE.js +63 -0
  72. package/dist/{chunk-AAZXTVJT.js → chunk-NG2AV7IB.js} +2 -72
  73. package/dist/chunk-ON6YKIQ7.js +260 -0
  74. package/dist/chunk-PDO5BLNM.js +22 -0
  75. package/dist/chunk-QNVHH5PU.js +20 -0
  76. package/dist/{chunk-P3YFQLEN.js → chunk-T533JLM3.js} +8 -10
  77. package/dist/chunk-UAMKJPDF.js +11 -0
  78. package/dist/chunk-VFH4ZKZT.js +66 -0
  79. package/dist/{chunk-UKHVMPX5.js → chunk-WGXMTFNU.js} +15 -17
  80. package/dist/{chunk-MNTINMWR.js → chunk-WJOSZOON.js} +184 -300
  81. package/dist/chunk-WTYEKPXU.js +451 -0
  82. package/dist/{chunk-IYM2SIVG.js → chunk-XIRPQQVE.js} +605 -363
  83. package/dist/cli/chat.js +24 -21
  84. package/dist/cli/check.js +4 -3
  85. package/dist/cli/init.js +12 -0
  86. package/dist/cli/plugins.js +12 -9
  87. package/dist/cli/snapshot-warmup.js +8 -6
  88. package/dist/cli/upgrade.js +265 -137
  89. package/dist/db/db.d.ts +4 -0
  90. package/dist/db/schema/conversations.d.ts +128 -0
  91. package/dist/db/schema.d.ts +129 -60
  92. package/dist/{db-2D6RMG67.js → db-BWXYIKFQ.js} +3 -3
  93. package/dist/detail-GA6NXWQJ.js +43 -0
  94. package/dist/discovery-RMHYSRL7.js +43 -0
  95. package/dist/handlers/health.d.ts +3 -0
  96. package/dist/instrumentation.js +1 -1
  97. package/dist/{legacy-import-VZ2MEGP7.js → legacy-import-3NQJVGQL.js} +4 -4
  98. package/dist/{api/people/list.js → list-A3WFRIYX.js} +26 -16
  99. package/dist/list-UXJI5Q5X.js +27 -0
  100. package/dist/nitro.d.ts +1 -1
  101. package/dist/nitro.js +2 -1
  102. package/dist/plugin-conversations-DDNGC35A.js +36 -0
  103. package/dist/{api/people/profile.js → profile-4RYDXWTF.js} +33 -18
  104. package/dist/reporting/plugin-conversations.d.ts +3 -0
  105. package/dist/reporting-schema.d.ts +140 -0
  106. package/dist/reporting.d.ts +12 -81
  107. package/dist/{runner-JZBRQP4F.js → runner-JALSYH2P.js} +17 -12
  108. package/dist/skills-MAZXZ2KE.js +26 -0
  109. package/dist/stats-XKXH3PTY.js +217 -0
  110. package/dist/subagent-TZ55XY37.js +48 -0
  111. package/dist/usage-schema.d.ts +25 -0
  112. package/dist/{validation-6XOOQGME.js → validation-FMEAEKCY.js} +4 -3
  113. package/migrations/0000_initial.sql +115 -0
  114. package/migrations/0001_conversation_metrics.sql +4 -0
  115. package/migrations/README.md +12 -30
  116. package/migrations/meta/0000_snapshot.json +1 -33
  117. package/migrations/meta/0001_snapshot.json +972 -0
  118. package/migrations/meta/_journal.json +9 -2
  119. package/package.json +13 -17
  120. package/dist/api/people/types.d.ts +0 -85
  121. package/dist/chat/advisor-request.d.ts +0 -4
  122. package/dist/chat/conversations/sql/migrations.d.ts +0 -22
  123. package/dist/chat/tools/advisor/session-store.d.ts +0 -9
  124. package/dist/chat/tools/advisor/tool.d.ts +0 -72
  125. package/dist/chunk-RAIB6WSG.js +0 -142
  126. package/dist/chunk-WM6KHPBQ.js +0 -48
  127. package/dist/db/schema/migrations.d.ts +0 -58
  128. package/dist/reporting/conversations/context.d.ts +0 -8
  129. package/dist/reporting/conversations/details.d.ts +0 -6
  130. package/dist/reporting/conversations/index.d.ts +0 -4
  131. package/dist/reporting/conversations/statistics.d.ts +0 -4
  132. package/dist/reporting/conversations/summaries.d.ts +0 -20
  133. package/dist/reporting/conversations/types.d.ts +0 -191
  134. package/dist/reporting/conversations.d.ts +0 -1
  135. package/dist/reporting.js +0 -1155
@@ -1,8 +1,9 @@
1
1
  import {
2
2
  getChatConfig,
3
3
  getConnectedStateContext,
4
- getStateAdapter
5
- } from "./chunk-YDVEH3SB.js";
4
+ getStateAdapter,
5
+ modelProfileSchema
6
+ } from "./chunk-EEZCY5WX.js";
6
7
  import {
7
8
  parseDestination,
8
9
  sameDestination
@@ -174,12 +175,16 @@ var juniorConversations = pgTable4(
174
175
  runId: text4("run_id"),
175
176
  lastCheckpointAt: timestamptz("last_checkpoint_at"),
176
177
  lastEnqueuedAt: timestamptz("last_enqueued_at"),
177
- // Subagent (advisor) runs are child conversations; top-level listings filter
178
- // parent_conversation_id IS NULL.
178
+ // Subagent runs are child conversations; top-level listings filter
179
+ // parent_conversation_id IS NULL. Historical advisor children use this too.
179
180
  parentConversationId: text4("parent_conversation_id").references(
180
181
  () => juniorConversations.conversationId
181
182
  ),
182
- transcriptPurgedAt: timestamptz("transcript_purged_at")
183
+ transcriptPurgedAt: timestamptz("transcript_purged_at"),
184
+ durationMs: integer("duration_ms").notNull().default(0),
185
+ usage: jsonb3("usage_json").$type(),
186
+ executionDurationMs: integer("execution_duration_ms").notNull().default(0),
187
+ executionUsage: jsonb3("execution_usage_json").$type()
183
188
  },
184
189
  (table) => [
185
190
  index3("junior_conversations_last_activity_idx").on(
@@ -255,14 +260,6 @@ var juniorConversationMessages = pgTable6(
255
260
  ]
256
261
  );
257
262
 
258
- // src/db/schema/migrations.ts
259
- import { pgTable as pgTable7, text as text7 } from "drizzle-orm/pg-core";
260
- var juniorSchemaMigrations = pgTable7("junior_schema_migrations", {
261
- id: text7("id").primaryKey(),
262
- checksum: text7("checksum").notNull(),
263
- appliedAt: timestamptz("applied_at").notNull().defaultNow()
264
- });
265
-
266
263
  // src/db/schema.ts
267
264
  var juniorSqlSchema = {
268
265
  juniorAgentSteps,
@@ -270,7 +267,6 @@ var juniorSqlSchema = {
270
267
  juniorConversations,
271
268
  juniorDestinations,
272
269
  juniorIdentities,
273
- juniorSchemaMigrations,
274
270
  juniorUsers
275
271
  };
276
272
 
@@ -379,396 +375,6 @@ async function upsertIdentity(executor, identity, nowMs = Date.now()) {
379
375
  };
380
376
  }
381
377
 
382
- // src/chat/conversations/sql/migrations.ts
383
- import { createHash } from "crypto";
384
- import { readFileSync } from "fs";
385
- import { fileURLToPath } from "url";
386
- import { z } from "zod";
387
- var MIGRATION_LOCK_NAME = "junior_conversation_schema";
388
- var migrationRecordSchema = z.object({
389
- id: z.string().min(1),
390
- checksum: z.string().min(1)
391
- }).strict();
392
- function checksumStatements(statements) {
393
- const hash = createHash("sha256");
394
- for (const statement of statements) {
395
- hash.update(statement);
396
- hash.update("\0");
397
- }
398
- return hash.digest("hex");
399
- }
400
- function defineMigration(id, statements) {
401
- return {
402
- id,
403
- checksum: checksumStatements(statements),
404
- statements
405
- };
406
- }
407
- var createMigrationTable = `
408
- CREATE TABLE IF NOT EXISTS junior_schema_migrations (
409
- id TEXT PRIMARY KEY,
410
- checksum TEXT NOT NULL,
411
- applied_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
412
- )
413
- `;
414
- var coreMetadataStatements = [
415
- `
416
- CREATE TABLE IF NOT EXISTS junior_identities (
417
- id TEXT PRIMARY KEY,
418
- kind TEXT NOT NULL,
419
- provider TEXT NOT NULL,
420
- provider_tenant_id TEXT NOT NULL DEFAULT '',
421
- provider_subject_id TEXT NOT NULL,
422
- display_name TEXT,
423
- handle TEXT,
424
- email TEXT,
425
- avatar_url TEXT,
426
- metadata_json JSONB,
427
- created_at TIMESTAMPTZ NOT NULL,
428
- updated_at TIMESTAMPTZ NOT NULL
429
- )
430
- `,
431
- `
432
- CREATE UNIQUE INDEX IF NOT EXISTS junior_identities_provider_subject_uidx
433
- ON junior_identities (provider, provider_tenant_id, provider_subject_id)
434
- `,
435
- `
436
- CREATE INDEX IF NOT EXISTS junior_identities_kind_provider_idx
437
- ON junior_identities (kind, provider)
438
- `,
439
- `
440
- CREATE TABLE IF NOT EXISTS junior_destinations (
441
- id TEXT PRIMARY KEY,
442
- provider TEXT NOT NULL,
443
- provider_tenant_id TEXT NOT NULL DEFAULT '',
444
- provider_destination_id TEXT NOT NULL,
445
- kind TEXT NOT NULL,
446
- parent_destination_id TEXT,
447
- display_name TEXT,
448
- visibility TEXT NOT NULL DEFAULT 'unknown',
449
- metadata_json JSONB,
450
- created_at TIMESTAMPTZ NOT NULL,
451
- updated_at TIMESTAMPTZ NOT NULL
452
- )
453
- `,
454
- `
455
- CREATE UNIQUE INDEX IF NOT EXISTS junior_destinations_provider_destination_uidx
456
- ON junior_destinations (provider, provider_tenant_id, provider_destination_id)
457
- `,
458
- `
459
- CREATE INDEX IF NOT EXISTS junior_destinations_provider_kind_idx
460
- ON junior_destinations (provider, kind)
461
- `,
462
- `
463
- CREATE TABLE IF NOT EXISTS junior_conversations (
464
- conversation_id TEXT PRIMARY KEY,
465
- schema_version INTEGER NOT NULL DEFAULT 1,
466
- source TEXT,
467
- origin_type TEXT,
468
- origin_id TEXT,
469
- origin_run_id TEXT,
470
- destination_id TEXT REFERENCES junior_destinations (id),
471
- destination_json JSONB,
472
- actor_identity_id TEXT REFERENCES junior_identities (id),
473
- requester_identity_id TEXT REFERENCES junior_identities (id),
474
- creator_identity_id TEXT REFERENCES junior_identities (id),
475
- credential_subject_identity_id TEXT REFERENCES junior_identities (id),
476
- requester_json JSONB,
477
- channel_name TEXT,
478
- title TEXT,
479
- created_at TIMESTAMPTZ NOT NULL,
480
- last_activity_at TIMESTAMPTZ NOT NULL,
481
- updated_at TIMESTAMPTZ NOT NULL,
482
- execution_updated_at TIMESTAMPTZ,
483
- execution_status TEXT NOT NULL,
484
- run_id TEXT,
485
- last_checkpoint_at TIMESTAMPTZ,
486
- last_enqueued_at TIMESTAMPTZ
487
- )
488
- `,
489
- `
490
- CREATE INDEX IF NOT EXISTS junior_conversations_last_activity_idx
491
- ON junior_conversations (last_activity_at DESC, conversation_id)
492
- `,
493
- `
494
- CREATE INDEX IF NOT EXISTS junior_conversations_active_idx
495
- ON junior_conversations (coalesce(execution_updated_at, updated_at) ASC, conversation_id)
496
- WHERE execution_status <> 'idle'
497
- `,
498
- `
499
- CREATE INDEX IF NOT EXISTS junior_conversations_destination_activity_idx
500
- ON junior_conversations (destination_id, last_activity_at DESC)
501
- `,
502
- `
503
- CREATE INDEX IF NOT EXISTS junior_conversations_actor_activity_idx
504
- ON junior_conversations (actor_identity_id, last_activity_at DESC)
505
- `,
506
- `
507
- CREATE INDEX IF NOT EXISTS junior_conversations_requester_activity_idx
508
- ON junior_conversations (requester_identity_id, last_activity_at DESC)
509
- `,
510
- `
511
- CREATE INDEX IF NOT EXISTS junior_conversations_origin_idx
512
- ON junior_conversations (origin_type, origin_id, last_activity_at DESC)
513
- `
514
- ];
515
- var destinationVisibilityBackfillStatements = [
516
- `
517
- UPDATE junior_destinations
518
- SET visibility = 'private'
519
- WHERE provider = 'slack'
520
- AND visibility = 'public'
521
- `
522
- ];
523
- var userIdentityStatements = [
524
- `
525
- CREATE TABLE IF NOT EXISTS junior_users (
526
- id TEXT PRIMARY KEY,
527
- primary_email TEXT NOT NULL,
528
- primary_email_normalized TEXT NOT NULL,
529
- display_name TEXT,
530
- created_at TIMESTAMPTZ NOT NULL,
531
- updated_at TIMESTAMPTZ NOT NULL
532
- )
533
- `,
534
- `
535
- CREATE UNIQUE INDEX IF NOT EXISTS junior_users_primary_email_normalized_uidx
536
- ON junior_users (primary_email_normalized)
537
- `,
538
- `
539
- ALTER TABLE junior_identities
540
- ADD COLUMN IF NOT EXISTS user_id TEXT REFERENCES junior_users (id)
541
- `,
542
- `
543
- ALTER TABLE junior_identities
544
- ADD COLUMN IF NOT EXISTS email_normalized TEXT
545
- `,
546
- `
547
- ALTER TABLE junior_identities
548
- ADD COLUMN IF NOT EXISTS email_verified BOOLEAN NOT NULL DEFAULT false
549
- `,
550
- `
551
- UPDATE junior_identities
552
- SET email_normalized = lower(trim(email)),
553
- email_verified = CASE
554
- WHEN kind = 'user' AND provider = 'slack' THEN true
555
- ELSE email_verified
556
- END
557
- WHERE email IS NOT NULL
558
- AND trim(email) <> ''
559
- `,
560
- `
561
- WITH first_identity AS (
562
- SELECT DISTINCT ON (email_normalized)
563
- id,
564
- COALESCE(NULLIF(trim(email), ''), email_normalized) AS email,
565
- email_normalized,
566
- display_name,
567
- created_at,
568
- updated_at
569
- FROM junior_identities
570
- WHERE kind = 'user'
571
- AND email_verified = true
572
- AND email_normalized IS NOT NULL
573
- ORDER BY email_normalized, created_at ASC, id ASC
574
- )
575
- INSERT INTO junior_users (
576
- id,
577
- primary_email,
578
- primary_email_normalized,
579
- display_name,
580
- created_at,
581
- updated_at
582
- )
583
- SELECT
584
- 'identity:' || id,
585
- email,
586
- email_normalized,
587
- display_name,
588
- created_at,
589
- updated_at
590
- FROM first_identity
591
- ON CONFLICT (primary_email_normalized) DO NOTHING
592
- `,
593
- `
594
- UPDATE junior_identities AS identity
595
- SET user_id = junior_users.id
596
- FROM junior_users
597
- WHERE identity.kind = 'user'
598
- AND identity.user_id IS NULL
599
- AND identity.email_verified = true
600
- AND identity.email_normalized = junior_users.primary_email_normalized
601
- `,
602
- `
603
- CREATE INDEX IF NOT EXISTS junior_identities_user_idx
604
- ON junior_identities (user_id)
605
- `,
606
- `
607
- CREATE INDEX IF NOT EXISTS junior_identities_verified_email_idx
608
- ON junior_identities (email_normalized)
609
- WHERE email_verified = true
610
- AND email_normalized IS NOT NULL
611
- `
612
- ];
613
- var actorCutoverStatements = [
614
- `
615
- UPDATE junior_conversations
616
- SET actor_identity_id = requester_identity_id
617
- WHERE requester_identity_id IS NOT NULL
618
- `,
619
- `
620
- DO $$
621
- BEGIN
622
- IF EXISTS (
623
- SELECT 1
624
- FROM information_schema.columns
625
- WHERE table_name = 'junior_conversations'
626
- AND column_name = 'requester_json'
627
- ) AND NOT EXISTS (
628
- SELECT 1
629
- FROM information_schema.columns
630
- WHERE table_name = 'junior_conversations'
631
- AND column_name = 'actor_json'
632
- ) THEN
633
- ALTER TABLE junior_conversations
634
- RENAME COLUMN requester_json TO actor_json;
635
- END IF;
636
- END $$;
637
- `,
638
- `
639
- ALTER TABLE junior_conversations
640
- ADD COLUMN IF NOT EXISTS actor_json JSONB
641
- `,
642
- `
643
- DO $$
644
- BEGIN
645
- IF EXISTS (
646
- SELECT 1
647
- FROM information_schema.columns
648
- WHERE table_name = 'junior_conversations'
649
- AND column_name = 'requester_json'
650
- ) THEN
651
- UPDATE junior_conversations
652
- SET actor_json = COALESCE(actor_json, requester_json);
653
- END IF;
654
- END $$;
655
- `,
656
- `
657
- DROP INDEX IF EXISTS junior_conversations_requester_activity_idx
658
- `,
659
- `
660
- ALTER TABLE junior_conversations
661
- DROP COLUMN IF EXISTS requester_identity_id
662
- `,
663
- `
664
- ALTER TABLE junior_conversations
665
- DROP COLUMN IF EXISTS requester_json
666
- `
667
- ];
668
- var conversationTranscriptStatements = [
669
- `
670
- CREATE TABLE IF NOT EXISTS junior_agent_steps (
671
- conversation_id TEXT NOT NULL REFERENCES junior_conversations (conversation_id),
672
- seq INTEGER NOT NULL,
673
- context_epoch INTEGER NOT NULL,
674
- type TEXT NOT NULL,
675
- role TEXT,
676
- payload JSONB NOT NULL,
677
- created_at TIMESTAMPTZ NOT NULL,
678
- PRIMARY KEY (conversation_id, seq)
679
- )
680
- `,
681
- `
682
- CREATE INDEX IF NOT EXISTS junior_agent_steps_epoch_idx
683
- ON junior_agent_steps (conversation_id, context_epoch, seq)
684
- `,
685
- `
686
- CREATE TABLE IF NOT EXISTS junior_conversation_messages (
687
- conversation_id TEXT NOT NULL REFERENCES junior_conversations (conversation_id),
688
- message_id TEXT NOT NULL,
689
- role TEXT NOT NULL,
690
- author_identity_id TEXT REFERENCES junior_identities (id),
691
- text TEXT NOT NULL,
692
- meta JSONB,
693
- replied_at TIMESTAMPTZ,
694
- created_at TIMESTAMPTZ NOT NULL,
695
- PRIMARY KEY (conversation_id, message_id)
696
- )
697
- `,
698
- `
699
- CREATE INDEX IF NOT EXISTS junior_conversation_messages_activity_idx
700
- ON junior_conversation_messages (conversation_id, created_at)
701
- `,
702
- `
703
- ALTER TABLE junior_conversations
704
- ADD COLUMN IF NOT EXISTS parent_conversation_id TEXT REFERENCES junior_conversations (conversation_id)
705
- `,
706
- `
707
- ALTER TABLE junior_conversations
708
- ADD COLUMN IF NOT EXISTS transcript_purged_at TIMESTAMPTZ
709
- `,
710
- `
711
- CREATE INDEX IF NOT EXISTS junior_conversations_parent_idx
712
- ON junior_conversations (parent_conversation_id)
713
- `
714
- ];
715
- var migrations = [
716
- defineMigration("0001_conversation_core", coreMetadataStatements),
717
- defineMigration(
718
- "0002_slack_destination_visibility_backfill",
719
- destinationVisibilityBackfillStatements
720
- ),
721
- defineMigration("0003_user_identities", userIdentityStatements),
722
- defineMigration("0004_actor_cutover", actorCutoverStatements),
723
- defineMigration(
724
- "0005_conversation_transcripts",
725
- conversationTranscriptStatements
726
- )
727
- ];
728
- function parseStoredMigrationRecord(value) {
729
- return migrationRecordSchema.parse(value);
730
- }
731
- async function listAppliedMigrations(executor) {
732
- const rows = await executor.query(
733
- "SELECT id, checksum FROM junior_schema_migrations ORDER BY id ASC"
734
- );
735
- const records = /* @__PURE__ */ new Map();
736
- for (const row of rows) {
737
- const record = parseStoredMigrationRecord(row);
738
- records.set(record.id, record);
739
- }
740
- return records;
741
- }
742
- async function applyMigration(executor, migration) {
743
- await executor.transaction(async () => {
744
- for (const statement of migration.statements) {
745
- await executor.execute(statement);
746
- }
747
- await executor.execute(
748
- "INSERT INTO junior_schema_migrations (id, checksum) VALUES ($1, $2)",
749
- [migration.id, migration.checksum]
750
- );
751
- });
752
- }
753
- async function migrateSchema(executor, migrationList = migrations) {
754
- await executor.withLock(MIGRATION_LOCK_NAME, async () => {
755
- await executor.execute(createMigrationTable);
756
- const applied = await listAppliedMigrations(executor);
757
- for (const migration of migrationList) {
758
- const existing = applied.get(migration.id);
759
- if (existing) {
760
- if (existing.checksum !== migration.checksum) {
761
- throw new Error(
762
- `Conversation migration ${migration.id} checksum changed`
763
- );
764
- }
765
- continue;
766
- }
767
- await applyMigration(executor, migration);
768
- }
769
- });
770
- }
771
-
772
378
  // src/chat/conversations/sql/store.ts
773
379
  var CONVERSATION_MUTATION_LOCK_PREFIX = "junior_conversation";
774
380
  function now() {
@@ -1007,29 +613,42 @@ function mergeActor(current2, next) {
1007
613
  ...current2.teamId ?? next.teamId ? { teamId: current2.teamId ?? next.teamId } : {}
1008
614
  };
1009
615
  }
616
+ function tokenTotal(usage) {
617
+ if (!usage) return 0;
618
+ if (usage.totalTokens !== void 0) return usage.totalTokens;
619
+ return (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0) + (usage.cachedInputTokens ?? 0) + (usage.cacheCreationTokens ?? 0);
620
+ }
621
+ function updateConversationUsage(args) {
622
+ const usage = {
623
+ totalTokens: tokenTotal(args.current) - tokenTotal(args.previousExecution) + tokenTotal(args.nextExecution)
624
+ };
625
+ if (args.current?.reasoningTokens !== void 0 || args.previousExecution?.reasoningTokens !== void 0 || args.nextExecution.reasoningTokens !== void 0) {
626
+ usage.reasoningTokens = (args.current?.reasoningTokens ?? 0) - (args.previousExecution?.reasoningTokens ?? 0) + (args.nextExecution.reasoningTokens ?? 0);
627
+ }
628
+ const costFields = [
629
+ "input",
630
+ "output",
631
+ "cacheRead",
632
+ "cacheWrite",
633
+ "total"
634
+ ];
635
+ const cost = {};
636
+ for (const field of costFields) {
637
+ if (args.current?.cost?.[field] === void 0 && args.previousExecution?.cost?.[field] === void 0 && args.nextExecution.cost?.[field] === void 0) {
638
+ continue;
639
+ }
640
+ cost[field] = Math.round(
641
+ ((args.current?.cost?.[field] ?? 0) - (args.previousExecution?.cost?.[field] ?? 0) + (args.nextExecution.cost?.[field] ?? 0)) * 1e12
642
+ ) / 1e12;
643
+ }
644
+ if (Object.keys(cost).length > 0) usage.cost = cost;
645
+ return usage;
646
+ }
1010
647
  var SqlStore = class {
1011
- constructor(executor, migrationExecutor) {
648
+ constructor(executor) {
1012
649
  this.executor = executor;
1013
- this.migrationExecutor = migrationExecutor;
1014
650
  }
1015
651
  executor;
1016
- migrationExecutor;
1017
- schemaReady;
1018
- /** Apply SQL schema migrations before runtime uses this store. */
1019
- async migrate() {
1020
- if (!this.schemaReady) {
1021
- this.schemaReady = migrateSchema(this.migrationExecutor);
1022
- }
1023
- const schemaReady = this.schemaReady;
1024
- try {
1025
- await schemaReady;
1026
- } catch (error) {
1027
- if (this.schemaReady === schemaReady) {
1028
- this.schemaReady = void 0;
1029
- }
1030
- throw error;
1031
- }
1032
- }
1033
652
  async get(args) {
1034
653
  const row = await this.readConversationRow(args.conversationId);
1035
654
  if (!row) {
@@ -1079,6 +698,13 @@ var SqlStore = class {
1079
698
  }
1080
699
  async recordExecution(args) {
1081
700
  await this.withConversationMutation(args.conversationId, async () => {
701
+ const existingRow = await this.readConversationRow(args.conversationId);
702
+ const existing = existingRow ? conversationFromRow(existingRow) : void 0;
703
+ const incomingExecutionAt = args.execution.updatedAtMs ?? args.updatedAtMs;
704
+ const existingExecutionAt = existing?.execution.updatedAtMs ?? existing?.updatedAtMs ?? 0;
705
+ const incomingIsFresh = incomingExecutionAt >= existingExecutionAt;
706
+ const sameRun = Boolean(existing?.execution.runId) && existing?.execution.runId === args.execution.runId;
707
+ const execution = incomingIsFresh ? args.execution : existing?.execution ?? args.execution;
1082
708
  await this.upsertConversation({
1083
709
  conversation: {
1084
710
  schemaVersion: 1,
@@ -1092,9 +718,28 @@ var SqlStore = class {
1092
718
  ...args.source ? { source: args.source } : {},
1093
719
  ...args.title ? { title: args.title } : {},
1094
720
  ...args.visibility ? { visibility: args.visibility } : {},
1095
- execution: args.execution
721
+ execution
1096
722
  }
1097
723
  });
724
+ if (incomingIsFresh && args.metrics) {
725
+ const row = existingRow?.conversation;
726
+ const usage = args.metrics.usage ? updateConversationUsage({
727
+ current: row?.usage ?? void 0,
728
+ previousExecution: sameRun ? row?.executionUsage ?? void 0 : void 0,
729
+ nextExecution: args.metrics.usage
730
+ }) : row?.usage ?? void 0;
731
+ await this.executor.db().update(juniorConversations).set({
732
+ durationMs: (row?.durationMs ?? 0) - (sameRun ? row?.executionDurationMs ?? 0 : 0) + args.metrics.durationMs,
733
+ usage: usage ?? null,
734
+ executionDurationMs: args.metrics.durationMs,
735
+ executionUsage: args.metrics.usage ?? (sameRun ? row?.executionUsage ?? null : null)
736
+ }).where(eq2(juniorConversations.conversationId, args.conversationId));
737
+ } else if (incomingIsFresh && !sameRun) {
738
+ await this.executor.db().update(juniorConversations).set({
739
+ executionDurationMs: 0,
740
+ executionUsage: null
741
+ }).where(eq2(juniorConversations.conversationId, args.conversationId));
742
+ }
1098
743
  });
1099
744
  }
1100
745
  async ensureChildConversation(args) {
@@ -1122,8 +767,8 @@ var SqlStore = class {
1122
767
  }
1123
768
  });
1124
769
  }
1125
- /** Copy one conversation record into SQL during backfill. */
1126
- async backfillConversation(sourceConversation) {
770
+ /** Copy one conversation record and retained metrics into SQL during backfill. */
771
+ async backfillConversation(sourceConversation, metrics) {
1127
772
  const { visibility: _visibility, ...conversation } = sourceConversation;
1128
773
  await this.withConversationMutation(
1129
774
  conversation.conversationId,
@@ -1156,6 +801,25 @@ var SqlStore = class {
1156
801
  execution: refreshExecutionFromSource ? conversation.execution : existing.execution
1157
802
  } : conversation;
1158
803
  await this.upsertConversation({ conversation: mergedConversation });
804
+ if (metrics) {
805
+ await this.executor.db().update(juniorConversations).set({
806
+ durationMs: metrics.durationMs,
807
+ usage: metrics.usage ?? null,
808
+ ...refreshExecutionFromSource ? {
809
+ executionDurationMs: metrics.executionDurationMs,
810
+ executionUsage: metrics.executionUsage ?? null
811
+ } : {}
812
+ }).where(
813
+ and2(
814
+ eq2(
815
+ juniorConversations.conversationId,
816
+ conversation.conversationId
817
+ ),
818
+ eq2(juniorConversations.durationMs, 0),
819
+ isNull(juniorConversations.usage)
820
+ )
821
+ );
822
+ }
1159
823
  }
1160
824
  );
1161
825
  }
@@ -1329,39 +993,39 @@ var SqlStore = class {
1329
993
  }
1330
994
  };
1331
995
  function createSqlStore(executor) {
1332
- return new SqlStore(executor, executor);
996
+ return new SqlStore(executor);
1333
997
  }
1334
998
 
1335
999
  // src/chat/conversations/sql/history.ts
1336
1000
  import { and as and3, asc as asc2, eq as eq3, sql as sql6 } from "drizzle-orm";
1337
1001
 
1338
1002
  // src/chat/conversations/history.ts
1339
- import { z as z4 } from "zod";
1003
+ import { z as z3 } from "zod";
1340
1004
 
1341
1005
  // src/chat/pi/messages.ts
1342
- import { z as z2 } from "zod";
1343
- var piMessageSchema = z2.object({
1344
- role: z2.string()
1006
+ import { z } from "zod";
1007
+ var piMessageSchema = z.object({
1008
+ role: z.string()
1345
1009
  }).passthrough().transform((value) => value);
1346
- var piContentMessageSchema = z2.object({
1347
- content: z2.array(z2.unknown()),
1348
- role: z2.string().min(1)
1010
+ var piContentMessageSchema = z.object({
1011
+ content: z.array(z.unknown()),
1012
+ role: z.string().min(1)
1349
1013
  }).passthrough().transform((value) => value);
1350
1014
 
1351
1015
  // src/chat/state/session-log.ts
1352
1016
  import { isDeepStrictEqual } from "util";
1353
- import { z as z3 } from "zod";
1017
+ import { z as z2 } from "zod";
1354
1018
  import { actorSchema } from "@sentry/junior-plugin-api";
1355
1019
  var AGENT_SESSION_LOG_PREFIX = "junior:agent-session-log";
1356
1020
  var AGENT_SESSION_LOG_SCHEMA_VERSION = 2;
1357
1021
  var INITIAL_SESSION_ID = "session_0";
1358
1022
  var STATE_STORE_LOCK_TTL_MS = 5e3;
1359
- var schemaVersionSchema = z3.union([z3.literal(1), z3.literal(2)]);
1360
- var piMessageAuthoritySchema = z3.union([
1361
- z3.literal("instruction"),
1362
- z3.literal("context")
1023
+ var schemaVersionSchema = z2.union([z2.literal(1), z2.literal(2)]);
1024
+ var piMessageAuthoritySchema = z2.union([
1025
+ z2.literal("instruction"),
1026
+ z2.literal("context")
1363
1027
  ]);
1364
- var piMessageProvenanceSchema = z3.object({
1028
+ var piMessageProvenanceSchema = z2.object({
1365
1029
  authority: piMessageAuthoritySchema,
1366
1030
  actor: actorSchema.optional()
1367
1031
  }).strict();
@@ -1387,109 +1051,109 @@ function legacyActorProvenance(actor) {
1387
1051
  }
1388
1052
  return unattributedContextProvenance;
1389
1053
  }
1390
- var piMessageEntrySchema = z3.object({
1054
+ var piMessageEntrySchema = z2.object({
1391
1055
  schemaVersion: schemaVersionSchema,
1392
- type: z3.literal("pi_message"),
1393
- sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1056
+ type: z2.literal("pi_message"),
1057
+ sessionId: z2.string().min(1).default(INITIAL_SESSION_ID),
1394
1058
  message: piMessageSchema,
1395
1059
  provenance: piMessageProvenanceSchema.optional(),
1396
1060
  // Legacy v1 latest-wins actor, decoded into provenance on read.
1397
1061
  actor: storedSlackActorSchema.optional()
1398
1062
  });
1399
- var projectionResetEntrySchema = z3.object({
1063
+ var projectionResetEntrySchema = z2.object({
1400
1064
  schemaVersion: schemaVersionSchema,
1401
- type: z3.literal("projection_reset"),
1402
- sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1403
- messages: z3.array(piMessageSchema),
1404
- provenance: z3.array(piMessageProvenanceSchema).optional(),
1065
+ type: z2.literal("projection_reset"),
1066
+ sessionId: z2.string().min(1).default(INITIAL_SESSION_ID),
1067
+ messages: z2.array(piMessageSchema),
1068
+ provenance: z2.array(piMessageProvenanceSchema).optional(),
1405
1069
  // Legacy v1 latest-wins actor; v1 resets carry no per-message provenance.
1406
1070
  actor: storedSlackActorSchema.optional()
1407
1071
  });
1408
- var actorRecordedEntrySchema = z3.object({
1072
+ var actorRecordedEntrySchema = z2.object({
1409
1073
  schemaVersion: schemaVersionSchema,
1410
- type: z3.literal("actor_recorded"),
1411
- sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1074
+ type: z2.literal("actor_recorded"),
1075
+ sessionId: z2.string().min(1).default(INITIAL_SESSION_ID),
1412
1076
  actor: storedSlackActorSchema
1413
1077
  });
1414
- var mcpProviderConnectedEntrySchema = z3.object({
1078
+ var mcpProviderConnectedEntrySchema = z2.object({
1415
1079
  schemaVersion: schemaVersionSchema,
1416
- type: z3.literal("mcp_provider_connected"),
1417
- sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1418
- provider: z3.string().min(1)
1080
+ type: z2.literal("mcp_provider_connected"),
1081
+ sessionId: z2.string().min(1).default(INITIAL_SESSION_ID),
1082
+ provider: z2.string().min(1)
1419
1083
  });
1420
- var authorizationKindSchema = z3.union([
1421
- z3.literal("plugin"),
1422
- z3.literal("mcp")
1084
+ var authorizationKindSchema = z2.union([
1085
+ z2.literal("plugin"),
1086
+ z2.literal("mcp")
1423
1087
  ]);
1424
- var authorizationRequestedEntrySchema = z3.object({
1088
+ var authorizationRequestedEntrySchema = z2.object({
1425
1089
  schemaVersion: schemaVersionSchema,
1426
- type: z3.literal("authorization_requested"),
1427
- sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1428
- createdAtMs: z3.number().int().nonnegative(),
1090
+ type: z2.literal("authorization_requested"),
1091
+ sessionId: z2.string().min(1).default(INITIAL_SESSION_ID),
1092
+ createdAtMs: z2.number().int().nonnegative(),
1429
1093
  kind: authorizationKindSchema,
1430
- provider: z3.string().min(1),
1431
- actorId: z3.string().min(1),
1432
- authorizationId: z3.string().min(1),
1433
- delivery: z3.union([
1434
- z3.literal("private_link_sent"),
1435
- z3.literal("private_link_reused")
1094
+ provider: z2.string().min(1),
1095
+ actorId: z2.string().min(1),
1096
+ authorizationId: z2.string().min(1),
1097
+ delivery: z2.union([
1098
+ z2.literal("private_link_sent"),
1099
+ z2.literal("private_link_reused")
1436
1100
  ])
1437
1101
  });
1438
- var authorizationCompletedEntrySchema = z3.object({
1102
+ var authorizationCompletedEntrySchema = z2.object({
1439
1103
  schemaVersion: schemaVersionSchema,
1440
- type: z3.literal("authorization_completed"),
1441
- sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1442
- createdAtMs: z3.number().int().nonnegative(),
1104
+ type: z2.literal("authorization_completed"),
1105
+ sessionId: z2.string().min(1).default(INITIAL_SESSION_ID),
1106
+ createdAtMs: z2.number().int().nonnegative(),
1443
1107
  kind: authorizationKindSchema,
1444
- provider: z3.string().min(1),
1445
- actorId: z3.string().min(1),
1446
- authorizationId: z3.string().min(1)
1108
+ provider: z2.string().min(1),
1109
+ actorId: z2.string().min(1),
1110
+ authorizationId: z2.string().min(1)
1447
1111
  });
1448
- var transcriptRefSchema = z3.object({
1449
- type: z3.literal("advisor_session"),
1450
- parentConversationId: z3.string().min(1),
1451
- key: z3.string().min(1)
1112
+ var transcriptRefSchema = z2.object({
1113
+ type: z2.literal("advisor_session"),
1114
+ parentConversationId: z2.string().min(1),
1115
+ key: z2.string().min(1)
1452
1116
  });
1453
- var toolExecutionStartedEntrySchema = z3.object({
1117
+ var toolExecutionStartedEntrySchema = z2.object({
1454
1118
  schemaVersion: schemaVersionSchema,
1455
- type: z3.literal("tool_execution_started"),
1456
- sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1457
- createdAtMs: z3.number().int().nonnegative(),
1458
- toolCallId: z3.string().min(1),
1459
- toolName: z3.string().min(1),
1460
- args: z3.unknown().optional()
1119
+ type: z2.literal("tool_execution_started"),
1120
+ sessionId: z2.string().min(1).default(INITIAL_SESSION_ID),
1121
+ createdAtMs: z2.number().int().nonnegative(),
1122
+ toolCallId: z2.string().min(1),
1123
+ toolName: z2.string().min(1),
1124
+ args: z2.unknown().optional()
1461
1125
  });
1462
- var subagentStartedEntrySchema = z3.object({
1126
+ var subagentStartedEntrySchema = z2.object({
1463
1127
  schemaVersion: schemaVersionSchema,
1464
- type: z3.literal("subagent_started"),
1465
- sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1466
- subagentInvocationId: z3.string().min(1),
1467
- subagentKind: z3.string().min(1),
1468
- parentToolCallId: z3.string().min(1).optional(),
1469
- parentConversationId: z3.string().min(1),
1470
- parentSessionId: z3.string().min(1).optional(),
1128
+ type: z2.literal("subagent_started"),
1129
+ sessionId: z2.string().min(1).default(INITIAL_SESSION_ID),
1130
+ subagentInvocationId: z2.string().min(1),
1131
+ subagentKind: z2.string().min(1),
1132
+ parentToolCallId: z2.string().min(1).optional(),
1133
+ parentConversationId: z2.string().min(1),
1134
+ parentSessionId: z2.string().min(1).optional(),
1471
1135
  transcriptRef: transcriptRefSchema,
1472
- historyMode: z3.literal("shared"),
1473
- modelId: z3.string().min(1).optional(),
1474
- reasoningLevel: z3.string().min(1).optional(),
1475
- createdAtMs: z3.number().int().nonnegative()
1136
+ historyMode: z2.literal("shared"),
1137
+ modelId: z2.string().min(1).optional(),
1138
+ reasoningLevel: z2.string().min(1).optional(),
1139
+ createdAtMs: z2.number().int().nonnegative()
1476
1140
  });
1477
- var subagentEndedEntrySchema = z3.object({
1141
+ var subagentEndedEntrySchema = z2.object({
1478
1142
  schemaVersion: schemaVersionSchema,
1479
- type: z3.literal("subagent_ended"),
1480
- sessionId: z3.string().min(1).default(INITIAL_SESSION_ID),
1481
- subagentInvocationId: z3.string().min(1),
1482
- outcome: z3.union([
1483
- z3.literal("success"),
1484
- z3.literal("error"),
1485
- z3.literal("aborted")
1143
+ type: z2.literal("subagent_ended"),
1144
+ sessionId: z2.string().min(1).default(INITIAL_SESSION_ID),
1145
+ subagentInvocationId: z2.string().min(1),
1146
+ outcome: z2.union([
1147
+ z2.literal("success"),
1148
+ z2.literal("error"),
1149
+ z2.literal("aborted")
1486
1150
  ]),
1487
- errorCode: z3.string().min(1).optional(),
1488
- transcriptEndMessageIndex: z3.number().int().nonnegative().optional(),
1489
- transcriptStartMessageIndex: z3.number().int().nonnegative().optional(),
1490
- createdAtMs: z3.number().int().nonnegative()
1151
+ errorCode: z2.string().min(1).optional(),
1152
+ transcriptEndMessageIndex: z2.number().int().nonnegative().optional(),
1153
+ transcriptStartMessageIndex: z2.number().int().nonnegative().optional(),
1154
+ createdAtMs: z2.number().int().nonnegative()
1491
1155
  });
1492
- var sessionLogEntrySchema = z3.discriminatedUnion("type", [
1156
+ var sessionLogEntrySchema = z2.discriminatedUnion("type", [
1493
1157
  piMessageEntrySchema,
1494
1158
  projectionResetEntrySchema,
1495
1159
  actorRecordedEntrySchema,
@@ -1651,82 +1315,134 @@ function instructionProvenanceFor(actor) {
1651
1315
  var contextProvenance = unattributedContextProvenance;
1652
1316
 
1653
1317
  // src/chat/conversations/history.ts
1654
- var piMessageStepEntrySchema = z4.object({
1655
- type: z4.literal("pi_message"),
1656
- schemaVersion: z4.number().int().optional(),
1318
+ var handoffModelProfileSchema = modelProfileSchema.refine(
1319
+ (profile) => profile !== "standard",
1320
+ "handoff profile must not be standard"
1321
+ );
1322
+ var piMessageStepEntrySchema = z3.object({
1323
+ type: z3.literal("pi_message"),
1324
+ schemaVersion: z3.number().int().optional(),
1657
1325
  message: piMessageSchema,
1658
1326
  provenance: piMessageProvenanceSchema.optional()
1659
1327
  });
1660
- var contextEpochStartedEntrySchema = z4.object({
1661
- type: z4.literal("context_epoch_started"),
1662
- reason: z4.union([z4.literal("compaction"), z4.literal("rollback")])
1663
- });
1664
- var mcpProviderConnectedEntrySchema2 = z4.object({
1665
- type: z4.literal("mcp_provider_connected"),
1666
- provider: z4.string().min(1)
1328
+ var contextEpochStartedEntrySchema = z3.union([
1329
+ z3.object({
1330
+ type: z3.literal("context_epoch_started"),
1331
+ reason: z3.literal("initial"),
1332
+ modelProfile: z3.literal("standard"),
1333
+ modelId: z3.string().min(1)
1334
+ }).strict(),
1335
+ z3.object({
1336
+ type: z3.literal("context_epoch_started"),
1337
+ reason: z3.literal("handoff"),
1338
+ modelProfile: handoffModelProfileSchema,
1339
+ modelId: z3.string().min(1)
1340
+ }).strict(),
1341
+ z3.object({
1342
+ type: z3.literal("context_epoch_started"),
1343
+ reason: z3.union([z3.literal("compaction"), z3.literal("rollback")]),
1344
+ // TODO(v0.97.0): Remove support for deployed compaction/rollback markers
1345
+ // without model bindings after those rows pass the retention horizon.
1346
+ modelProfile: z3.undefined().optional(),
1347
+ modelId: z3.undefined().optional()
1348
+ }).strict(),
1349
+ z3.object({
1350
+ type: z3.literal("context_epoch_started"),
1351
+ reason: z3.union([z3.literal("compaction"), z3.literal("rollback")]),
1352
+ modelProfile: modelProfileSchema,
1353
+ modelId: z3.string().min(1)
1354
+ }).strict()
1355
+ ]);
1356
+ var piMessageStepSchema = z3.object({
1357
+ message: piMessageSchema,
1358
+ createdAtMs: z3.number().finite(),
1359
+ provenance: piMessageProvenanceSchema.optional()
1360
+ }).strict();
1361
+ var contextEpochStartSchema = z3.discriminatedUnion("reason", [
1362
+ z3.object({
1363
+ reason: z3.literal("initial"),
1364
+ modelProfile: z3.literal("standard"),
1365
+ modelId: z3.string().min(1),
1366
+ messages: z3.array(piMessageStepSchema)
1367
+ }).strict(),
1368
+ z3.object({
1369
+ reason: z3.literal("handoff"),
1370
+ modelProfile: handoffModelProfileSchema,
1371
+ modelId: z3.string().min(1),
1372
+ messages: z3.array(piMessageStepSchema)
1373
+ }).strict(),
1374
+ z3.object({
1375
+ reason: z3.union([z3.literal("compaction"), z3.literal("rollback")]),
1376
+ modelProfile: modelProfileSchema,
1377
+ modelId: z3.string().min(1),
1378
+ messages: z3.array(piMessageStepSchema)
1379
+ }).strict()
1380
+ ]);
1381
+ var mcpProviderConnectedEntrySchema2 = z3.object({
1382
+ type: z3.literal("mcp_provider_connected"),
1383
+ provider: z3.string().min(1)
1667
1384
  });
1668
- var authorizationKindSchema2 = z4.union([
1669
- z4.literal("plugin"),
1670
- z4.literal("mcp")
1385
+ var authorizationKindSchema2 = z3.union([
1386
+ z3.literal("plugin"),
1387
+ z3.literal("mcp")
1671
1388
  ]);
1672
- var authorizationRequestedEntrySchema2 = z4.object({
1673
- type: z4.literal("authorization_requested"),
1389
+ var authorizationRequestedEntrySchema2 = z3.object({
1390
+ type: z3.literal("authorization_requested"),
1674
1391
  kind: authorizationKindSchema2,
1675
- provider: z4.string().min(1),
1676
- actorId: z4.string().min(1),
1677
- authorizationId: z4.string().min(1),
1678
- delivery: z4.union([
1679
- z4.literal("private_link_sent"),
1680
- z4.literal("private_link_reused")
1392
+ provider: z3.string().min(1),
1393
+ actorId: z3.string().min(1),
1394
+ authorizationId: z3.string().min(1),
1395
+ delivery: z3.union([
1396
+ z3.literal("private_link_sent"),
1397
+ z3.literal("private_link_reused")
1681
1398
  ])
1682
1399
  });
1683
- var authorizationCompletedEntrySchema2 = z4.object({
1684
- type: z4.literal("authorization_completed"),
1400
+ var authorizationCompletedEntrySchema2 = z3.object({
1401
+ type: z3.literal("authorization_completed"),
1685
1402
  kind: authorizationKindSchema2,
1686
- provider: z4.string().min(1),
1687
- actorId: z4.string().min(1),
1688
- authorizationId: z4.string().min(1)
1403
+ provider: z3.string().min(1),
1404
+ actorId: z3.string().min(1),
1405
+ authorizationId: z3.string().min(1)
1689
1406
  });
1690
- var toolExecutionStartedEntrySchema2 = z4.object({
1691
- type: z4.literal("tool_execution_started"),
1692
- toolCallId: z4.string().min(1),
1693
- toolName: z4.string().min(1),
1694
- args: z4.unknown().optional()
1407
+ var toolExecutionStartedEntrySchema2 = z3.object({
1408
+ type: z3.literal("tool_execution_started"),
1409
+ toolCallId: z3.string().min(1),
1410
+ toolName: z3.string().min(1),
1411
+ args: z3.unknown().optional()
1695
1412
  });
1696
- var visibleContextCompactedEntrySchema = z4.object({
1697
- type: z4.literal("visible_context_compacted"),
1698
- compactions: z4.array(
1699
- z4.object({
1700
- coveredMessageIds: z4.array(z4.string()),
1701
- createdAtMs: z4.number(),
1702
- id: z4.string().min(1),
1703
- summary: z4.string()
1413
+ var visibleContextCompactedEntrySchema = z3.object({
1414
+ type: z3.literal("visible_context_compacted"),
1415
+ compactions: z3.array(
1416
+ z3.object({
1417
+ coveredMessageIds: z3.array(z3.string()),
1418
+ createdAtMs: z3.number(),
1419
+ id: z3.string().min(1),
1420
+ summary: z3.string()
1704
1421
  })
1705
1422
  )
1706
1423
  });
1707
- var subagentStartedEntrySchema2 = z4.object({
1708
- type: z4.literal("subagent_started"),
1709
- subagentInvocationId: z4.string().min(1),
1710
- subagentKind: z4.string().min(1),
1711
- modelId: z4.string().min(1).optional(),
1712
- parentToolCallId: z4.string().min(1).optional(),
1713
- reasoningLevel: z4.string().min(1).optional(),
1714
- childConversationId: z4.string().min(1),
1715
- historyMode: z4.literal("shared")
1716
- });
1717
- var subagentEndedEntrySchema2 = z4.object({
1718
- type: z4.literal("subagent_ended"),
1719
- subagentInvocationId: z4.string().min(1),
1720
- outcome: z4.union([
1721
- z4.literal("success"),
1722
- z4.literal("error"),
1723
- z4.literal("aborted")
1424
+ var subagentStartedEntrySchema2 = z3.object({
1425
+ type: z3.literal("subagent_started"),
1426
+ subagentInvocationId: z3.string().min(1),
1427
+ subagentKind: z3.string().min(1),
1428
+ modelId: z3.string().min(1).optional(),
1429
+ parentToolCallId: z3.string().min(1).optional(),
1430
+ reasoningLevel: z3.string().min(1).optional(),
1431
+ childConversationId: z3.string().min(1),
1432
+ historyMode: z3.union([z3.literal("isolated"), z3.literal("shared")])
1433
+ }).strict();
1434
+ var subagentEndedEntrySchema2 = z3.object({
1435
+ type: z3.literal("subagent_ended"),
1436
+ subagentInvocationId: z3.string().min(1),
1437
+ outcome: z3.union([
1438
+ z3.literal("success"),
1439
+ z3.literal("error"),
1440
+ z3.literal("aborted")
1724
1441
  ]),
1725
- errorCode: z4.string().min(1).optional()
1442
+ errorCode: z3.string().min(1).optional()
1726
1443
  });
1727
- var agentStepEntrySchema = z4.discriminatedUnion("type", [
1444
+ var appendableAgentStepEntrySchema = z3.union([
1728
1445
  piMessageStepEntrySchema,
1729
- contextEpochStartedEntrySchema,
1730
1446
  mcpProviderConnectedEntrySchema2,
1731
1447
  authorizationRequestedEntrySchema2,
1732
1448
  authorizationCompletedEntrySchema2,
@@ -1735,6 +1451,14 @@ var agentStepEntrySchema = z4.discriminatedUnion("type", [
1735
1451
  subagentStartedEntrySchema2,
1736
1452
  subagentEndedEntrySchema2
1737
1453
  ]);
1454
+ var agentStepEntrySchema = z3.union([
1455
+ appendableAgentStepEntrySchema,
1456
+ contextEpochStartedEntrySchema
1457
+ ]);
1458
+ var newAgentStepSchema = z3.object({
1459
+ entry: appendableAgentStepEntrySchema,
1460
+ createdAtMs: z3.number().finite()
1461
+ }).strict();
1738
1462
 
1739
1463
  // src/chat/conversations/sql/conversation-row.ts
1740
1464
  import { sql as sql5 } from "drizzle-orm";
@@ -1801,11 +1525,12 @@ var SqlAgentStepStore = class {
1801
1525
  }
1802
1526
  executor;
1803
1527
  async append(conversationId, steps) {
1804
- if (steps.length === 0) {
1528
+ const parsed = steps.map((step) => newAgentStepSchema.parse(step));
1529
+ if (parsed.length === 0) {
1805
1530
  return;
1806
1531
  }
1807
1532
  const newestCreatedAtMs = Math.max(
1808
- ...steps.map((step) => step.createdAtMs)
1533
+ ...parsed.map((step) => step.createdAtMs)
1809
1534
  );
1810
1535
  await this.executor.transaction(async () => {
1811
1536
  await ensureConversationRow(
@@ -1816,23 +1541,25 @@ var SqlAgentStepStore = class {
1816
1541
  const cursor = await this.readCursor(conversationId);
1817
1542
  const contextEpoch = cursor.maxEpoch ?? 0;
1818
1543
  let seq = cursor.nextSeq;
1819
- const rows = steps.map(
1544
+ const rows = parsed.map(
1820
1545
  (step) => insertFromStep(conversationId, seq++, contextEpoch, step)
1821
1546
  );
1822
1547
  await this.executor.db().insert(juniorAgentSteps).values(rows);
1823
1548
  });
1824
1549
  }
1825
1550
  async startEpoch(conversationId, opts) {
1551
+ const parsed = contextEpochStartSchema.parse(opts);
1826
1552
  await this.executor.transaction(async () => {
1827
1553
  await ensureConversationRow(this.executor, conversationId, Date.now());
1828
1554
  const cursor = await this.readCursor(conversationId);
1829
- const contextEpoch = (cursor.maxEpoch ?? -1) + 1;
1555
+ const contextEpoch = parsed.reason === "initial" ? cursor.maxEpoch ?? 0 : (cursor.maxEpoch ?? -1) + 1;
1830
1556
  let seq = cursor.nextSeq;
1557
+ const { messages, ...binding } = parsed;
1831
1558
  const marker = {
1832
- entry: { type: "context_epoch_started", reason: opts.reason },
1559
+ entry: { type: "context_epoch_started", ...binding },
1833
1560
  createdAtMs: Date.now()
1834
1561
  };
1835
- const rows = [marker, ...opts.messages.map(piMessageStep)].map(
1562
+ const rows = [marker, ...messages.map(piMessageStep)].map(
1836
1563
  (step) => insertFromStep(conversationId, seq++, contextEpoch, step)
1837
1564
  );
1838
1565
  await this.executor.db().insert(juniorAgentSteps).values(rows);
@@ -1957,6 +1684,7 @@ import {
1957
1684
  Pool
1958
1685
  } from "@neondatabase/serverless";
1959
1686
  import { drizzle } from "drizzle-orm/neon-serverless";
1687
+ import { migrate } from "drizzle-orm/neon-serverless/migrator";
1960
1688
  var NeonExecutor = class {
1961
1689
  constructor(pool) {
1962
1690
  this.pool = pool;
@@ -1978,6 +1706,12 @@ var NeonExecutor = class {
1978
1706
  ]);
1979
1707
  return result.rows;
1980
1708
  }
1709
+ async migrate(config) {
1710
+ await migrate(
1711
+ drizzle(this.queryClient(), { schema: juniorSqlSchema }),
1712
+ config
1713
+ );
1714
+ }
1981
1715
  async transaction(callback) {
1982
1716
  const existingClient = this.transactionClient.getStore();
1983
1717
  if (existingClient) {
@@ -2037,6 +1771,16 @@ var NeonExecutor = class {
2037
1771
  client.release();
2038
1772
  }
2039
1773
  }
1774
+ async withMigrationLock(migrationTable, callback) {
1775
+ const client = await this.pool.connect();
1776
+ const lockName = `junior:migrate:${migrationTable}`;
1777
+ try {
1778
+ await client.query("SELECT pg_advisory_lock(hashtext($1))", [lockName]);
1779
+ return await callback();
1780
+ } finally {
1781
+ client.release(true);
1782
+ }
1783
+ }
2040
1784
  async close() {
2041
1785
  await this.pool.end();
2042
1786
  }
@@ -2057,6 +1801,7 @@ function createNeonJuniorSqlExecutor(args) {
2057
1801
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
2058
1802
  import pg from "pg";
2059
1803
  import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
1804
+ import { migrate as migrate2 } from "drizzle-orm/node-postgres/migrator";
2060
1805
  var { Pool: Pool2 } = pg;
2061
1806
  var PostgresExecutor = class {
2062
1807
  constructor(pool) {
@@ -2079,6 +1824,12 @@ var PostgresExecutor = class {
2079
1824
  ]);
2080
1825
  return result.rows;
2081
1826
  }
1827
+ async migrate(config) {
1828
+ await migrate2(
1829
+ drizzle2(this.queryClient(), { schema: juniorSqlSchema }),
1830
+ config
1831
+ );
1832
+ }
2082
1833
  async transaction(callback) {
2083
1834
  const existingClient = this.transactionClient.getStore();
2084
1835
  if (existingClient) {
@@ -2138,6 +1889,16 @@ var PostgresExecutor = class {
2138
1889
  client.release();
2139
1890
  }
2140
1891
  }
1892
+ async withMigrationLock(migrationTable, callback) {
1893
+ const client = await this.pool.connect();
1894
+ const lockName = `junior:migrate:${migrationTable}`;
1895
+ try {
1896
+ await client.query("SELECT pg_advisory_lock(hashtext($1))", [lockName]);
1897
+ return await callback();
1898
+ } finally {
1899
+ client.release(true);
1900
+ }
1901
+ }
2141
1902
  async close() {
2142
1903
  await this.pool.end();
2143
1904
  }
@@ -2222,8 +1983,8 @@ export {
2222
1983
  juniorConversations,
2223
1984
  juniorAgentSteps,
2224
1985
  juniorConversationMessages,
2225
- migrateSchema,
2226
1986
  createSqlStore,
1987
+ piMessageSchema,
2227
1988
  legacyActorProvenance,
2228
1989
  instructionActors,
2229
1990
  readSessionLogEntries,