@sentry/junior 0.271.0 → 0.273.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.
Files changed (75) hide show
  1. package/dist/{adapter-Bqbtr1RV.js → adapter-umap1cth.js} +1 -1
  2. package/dist/{admin-CWBjzyN0.js → admin-0o6E_3lo.js} +1 -1
  3. package/dist/{agent-runner-BiHsH8O_.js → agent-runner-D7Qz-lGO.js} +899 -631
  4. package/dist/api/schema.d.ts +2 -2
  5. package/dist/api/schema.js +5 -5
  6. package/dist/api.d.ts +1 -1
  7. package/dist/api.js +62 -537
  8. package/dist/{app-C81xfpNa.d.ts → app-BcyhJq8x.d.ts} +4 -9
  9. package/dist/app.d.ts +1 -1
  10. package/dist/app.js +673 -134
  11. package/dist/{assistant-reply-DgtX9DaJ.js → assistant-reply-wZ7i4OFa.js} +1 -1
  12. package/dist/{automation-Dwh_xcqM.js → automation-yffXcZt9.js} +1 -1
  13. package/dist/{briefs-0FeSJHQ3.js → briefs-CgMnlpIp.js} +4 -4
  14. package/dist/{catalog-runtime-Bw9kjGVu.js → catalog-runtime-TygDsPOU.js} +1 -1
  15. package/dist/cli/chat.js +15 -15
  16. package/dist/cli/check.js +3 -3
  17. package/dist/cli/init.js +1 -1
  18. package/dist/cli/main.js +3 -3
  19. package/dist/cli/plugins.js +4 -4
  20. package/dist/cli/snapshot-create.js +4 -4
  21. package/dist/cli/upgrade.d.ts +1 -1
  22. package/dist/cli/upgrade.js +3 -3
  23. package/dist/{client-B0jlQSgO.js → client-DhqbV0Fe.js} +63 -5
  24. package/dist/{reporting-schema-CZREhYc-.js → common-Eu6sx09c.js} +4 -1
  25. package/dist/{config-DrAeD8sr.js → config-C6FTd7PX.js} +7 -15
  26. package/dist/{connections-BzN2K0G9.js → connections-Dq72KajR.js} +11 -11
  27. package/dist/{conversation-AhOVzN_k.js → conversation-T4oabbBi.js} +22 -1
  28. package/dist/{conversation-privacy-D1NhUxyo.js → conversation-privacy-BeRgrsph.js} +1 -1
  29. package/dist/{credential-sync-DpNWl4yy.js → credential-sync-BmCbHAp4.js} +15 -16
  30. package/dist/{db-DSabI7jJ.d.ts → db-DOzBGLn5.d.ts} +11 -1
  31. package/dist/{executor-C2cRw0hZ.js → executor-BafgvQcc.js} +25 -7
  32. package/dist/instrumentation.js +114 -3
  33. package/dist/{job-queue-CEeLYkSj.js → job-queue-C4lphW9Y.js} +3 -3
  34. package/dist/{logging-DuwXUuIY.js → logging-CNs1e04n.js} +6 -1
  35. package/dist/message-CETC5lsl.js +186 -0
  36. package/dist/nitro.d.ts +1 -1
  37. package/dist/nitro.js +3 -3
  38. package/dist/{oauth-callback-server-oM5TJt34.js → oauth-callback-server-DcQZsKMm.js} +2 -2
  39. package/dist/{credentialed-mh7HBqey.js → projection-BOpi891B.js} +4576 -49
  40. package/dist/{prompt-BPSdXYXV.js → prompt-B1ySjUyf.js} +1 -1
  41. package/dist/{registry-BPtbAVFo.js → registry-B-yUrv3h.js} +2 -2
  42. package/dist/{resolve-D44G1jJm.js → resolve-DqVtd4Q9.js} +3 -3
  43. package/dist/{result-CvP8c5bb.js → result-DQwTiLTw.js} +16 -12
  44. package/dist/{runner-FIGIKvk2.js → runner-Dco5qMq6.js} +11 -11
  45. package/dist/{sandbox-egress-signals-C_JzucwP.js → sandbox-egress-signals-DFc_9Mcv.js} +1 -1
  46. package/dist/{schema-DKZgO-xf.js → schema-5xHDGlBW.js} +10 -9
  47. package/dist/{schema-DPCvePop.d.ts → schema-9OGdsaxF.d.ts} +28 -3
  48. package/dist/{service-account-1mJGRAvx.js → service-account-CWfk9Cs-.js} +3 -3
  49. package/dist/{service-account-CHO3YXpt.js → service-account-smdPFWNy.js} +3 -3
  50. package/dist/{session-stop-DuUB1rwQ.js → session-stop-DaT_7tYa.js} +50 -24
  51. package/dist/{skills-DHK7L9Eo.js → skills-BbtJSF2-.js} +2 -2
  52. package/dist/{spawn-CKl_ySIq.js → spawn-Ceikdjkp.js} +2 -2
  53. package/dist/{list-DjJJ5K0x.js → stop-dKUc-x2K.js} +1017 -469
  54. package/dist/{store-Di0E4EvU.js → store-C1xuZcLd.js} +48 -31
  55. package/dist/{store-BF2XEeQz.js → store-DaxUuOmD.js} +3 -3
  56. package/dist/{task-queue-CC7IFtbT.js → task-queue-ymAh_-P1.js} +4 -4
  57. package/dist/{task-runner-DKZtMFwa.js → task-runner-vlH6pEm3.js} +6 -6
  58. package/dist/{thread-state-BQ1T3eeF.js → thread-state-BkUpE9Ae.js} +4 -5
  59. package/dist/{turn-failure-response-B5eSFoHn.js → turn-failure-response-DIsD7ip_.js} +7 -8
  60. package/dist/{turn-lifecycle-CTLEIEOn.js → turn-lifecycle-BdPKdPS8.js} +1 -1
  61. package/dist/turn-session-routing-BdCTDq9m.js +1625 -0
  62. package/dist/{validation-Cjo-3yt3.js → validation-BQjCfh84.js} +1 -1
  63. package/dist/{version-D30MBLLw.js → version-BpiFGZZW.js} +1 -1
  64. package/dist/version.js +1 -1
  65. package/dist/{viewer-D3jhuP66.js → viewer-C6Y1aXjZ.js} +49 -21
  66. package/dist/{work-CL9QwQcl.js → work-ChPh_YEh.js} +27 -39
  67. package/dist/{worker-S2P7IJDI.js → worker-Be_sK_ou.js} +6 -6
  68. package/migrations/0072_conversation_read_positions.sql +10 -0
  69. package/migrations/meta/0072_snapshot.json +4064 -0
  70. package/migrations/meta/_journal.json +7 -0
  71. package/package.json +4 -4
  72. package/dist/message-CRe3WBKc.js +0 -59
  73. package/dist/turn-session-routing-BFVp8TbU.js +0 -6072
  74. package/migrations/AGENTS.md +0 -8
  75. package/dist/{oauth-relay-CTygMkNx.js → oauth-relay-C6ZAyJgf.js} +1 -1
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.js";
2
- import { n as pluginCatalogRuntime } from "./catalog-runtime-Bw9kjGVu.js";
2
+ import { n as pluginCatalogRuntime } from "./catalog-runtime-TygDsPOU.js";
3
3
  import { isDeepStrictEqual } from "node:util";
4
4
  // src/chat/plugins/validation.ts
5
5
  var validation_exports = /* @__PURE__ */ __exportAll({
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.271.0";
2
+ var version = "0.273.0";
3
3
  // src/version.ts
4
4
  /** Installed Junior package version, or `unknown` when package metadata is unavailable. */
5
5
  const JUNIOR_VERSION = typeof version === "string" && version.trim() ? version : "unknown";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export { t as JUNIOR_VERSION } from "./version-D30MBLLw.js";
1
+ export { t as JUNIOR_VERSION } from "./version-BpiFGZZW.js";
@@ -1,9 +1,9 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.js";
2
- import { i as getChatConfig } from "./config-DrAeD8sr.js";
3
- import { L as GOOGLE_IDENTITY_PROVIDER, h as logWarn, z as normalizeIdentityEmail } from "./logging-DuwXUuIY.js";
2
+ import { i as getChatConfig } from "./config-C6FTd7PX.js";
3
+ import { L as GOOGLE_IDENTITY_PROVIDER, h as logWarn, z as normalizeIdentityEmail } from "./logging-CNs1e04n.js";
4
4
  import { a as isSlackUserId, d as parseSlackChannelReferenceId, f as parseSlackTeamId, s as parseActorUserId } from "./actor-B4DYygOB.js";
5
5
  import { c as juniorIdentities, d as juniorDestinations, l as juniorUsers, o as conversationReadColumns, s as juniorConversations } from "./agent-invocations-C1RNOCs8.js";
6
- import { _ as CONVERSATION_METRICS, h as juniorConversationBindings, m as juniorConversationParticipants, t as createJuniorSqlExecutor, v as juniorConversationMetrics, x as juniorConversationAnnotations, y as juniorConversationEvents } from "./executor-C2cRw0hZ.js";
6
+ import { S as juniorConversationAnnotations, b as juniorConversationEvents, g as juniorConversationBindings, h as juniorConversationParticipants, t as createJuniorSqlExecutor, v as CONVERSATION_METRICS, y as juniorConversationMetrics } from "./executor-BafgvQcc.js";
7
7
  import { d as newConversationEventSchema, f as encodeHistoryPayload, l as decodeStoredConversationEvent, n as detectImageMediaType, o as conversationEventDataSchema, u as historyReplacementSchema, v as usageSchema } from "./media-U6Tuz6Mi.js";
8
8
  import { a as withLock } from "./locks-D-0h111u.js";
9
9
  import { createHash, randomUUID } from "node:crypto";
@@ -995,6 +995,9 @@ var SqlStore = class {
995
995
  if (!row) return;
996
996
  if (row.visibility === "public" || row.visibility === "private") return row.visibility;
997
997
  }
998
+ async recordDestinationVisibility(args) {
999
+ await this.upsertDestination(destinationUpsertFromDestination(args), args.nowMs ?? Date.now());
1000
+ }
998
1001
  async findSlackDestinationByName(args) {
999
1002
  const normalized = args.channelName.trim().replace(/^#/, "").toLowerCase();
1000
1003
  if (!normalized) return;
@@ -1406,20 +1409,33 @@ var SqlConversationEventStore = class {
1406
1409
  nowMs: event.createdAtMs
1407
1410
  });
1408
1411
  rows.push(insertFromEvent(conversationId, seq++, historyVersion, event, actorIdentityId));
1409
- if (actorIdentityId) await recordConversationParticipant(this.executor, {
1410
- actorIdentityId,
1411
- conversationId,
1412
- atMs: event.createdAtMs,
1413
- restoreArchive: eventUnarchivesConversation(event.data)
1414
- });
1415
1412
  }
1416
1413
  await this.executor.db().insert(juniorConversationEvents).values(rows);
1414
+ for (const event of pending) if (event.data.type === "turn_started") await this.recordTurnInputParticipants(conversationId, event.data.inputMessageIds);
1417
1415
  return rows.map((row) => ({
1418
1416
  seq: row.seq,
1419
1417
  historyVersion: row.historyVersion
1420
1418
  }));
1421
1419
  });
1422
1420
  }
1421
+ /**
1422
+ * Add the authors of Turn input messages to the conversation.
1423
+ * Only people who start a Turn join it. Thread context, such as backfilled
1424
+ * or skipped messages, keeps its author without membership.
1425
+ */
1426
+ async recordTurnInputParticipants(conversationId, inputMessageIds) {
1427
+ const rows = await this.executor.db().select().from(juniorConversationEvents).where(and(eq(juniorConversationEvents.conversationId, conversationId), eq(juniorConversationEvents.type, "message"), inArray(sql`${juniorConversationEvents.payload}->>'messageId'`, [...inputMessageIds])));
1428
+ for (const row of rows) {
1429
+ const { data } = eventFromRow(row);
1430
+ if (!row.actorIdentityId || data.type !== "message") continue;
1431
+ await recordConversationParticipant(this.executor, {
1432
+ actorIdentityId: row.actorIdentityId,
1433
+ conversationId,
1434
+ atMs: row.createdAt.getTime(),
1435
+ restoreArchive: eventUnarchivesConversation(data)
1436
+ });
1437
+ }
1438
+ }
1423
1439
  async replaceHistory(conversationId, replacement) {
1424
1440
  const parsed = historyReplacementSchema.parse(replacement);
1425
1441
  await withConversationEventLock(this.executor, conversationId, async () => {
@@ -1727,8 +1743,14 @@ function parseAvatarUrl(value, provider) {
1727
1743
  const host = url.hostname.toLowerCase();
1728
1744
  return hosts.some((allowed) => host === allowed || host.endsWith(`.${allowed}`)) ? url.href : void 0;
1729
1745
  }
1730
- function avatarPath(identityId) {
1731
- return `/api/people/avatars/${encodeURIComponent(identityId)}`;
1746
+ function avatarUrlHash(url) {
1747
+ return createHash("sha256").update(url).digest("hex");
1748
+ }
1749
+ function avatarVersion(url) {
1750
+ return avatarUrlHash(url).slice(0, 16);
1751
+ }
1752
+ function avatarPath(identityId, avatarUrl) {
1753
+ return `/api/people/avatars/${encodeURIComponent(identityId)}?v=${avatarVersion(avatarUrl)}`;
1732
1754
  }
1733
1755
  /**
1734
1756
  * Read the photo path of each user: the Slack photo, then the Google photo.
@@ -1737,6 +1759,7 @@ function avatarPath(identityId) {
1737
1759
  async function readUserAvatarUrls(db, userIds) {
1738
1760
  if (userIds.length === 0) return /* @__PURE__ */ new Map();
1739
1761
  const rows = await db.select({
1762
+ avatarUrl: juniorIdentities.avatarUrl,
1740
1763
  identityId: juniorIdentities.id,
1741
1764
  provider: juniorIdentities.provider,
1742
1765
  updatedAt: juniorIdentities.updatedAt,
@@ -1745,7 +1768,7 @@ async function readUserAvatarUrls(db, userIds) {
1745
1768
  const rank = (row) => AVATAR_PROVIDERS.indexOf(row.provider);
1746
1769
  rows.sort((left, right) => rank(left) - rank(right) || right.updatedAt.getTime() - left.updatedAt.getTime());
1747
1770
  const avatars = /* @__PURE__ */ new Map();
1748
- for (const row of rows) if (row.userId && !avatars.has(row.userId)) avatars.set(row.userId, avatarPath(row.identityId));
1771
+ for (const row of rows) if (row.userId && row.avatarUrl && !avatars.has(row.userId)) avatars.set(row.userId, avatarPath(row.identityId, row.avatarUrl));
1749
1772
  return avatars;
1750
1773
  }
1751
1774
  /**
@@ -1754,7 +1777,7 @@ async function readUserAvatarUrls(db, userIds) {
1754
1777
  */
1755
1778
  function identityAvatarUrl(row, userAvatarUrls) {
1756
1779
  if (row.userId) return userAvatarUrls.get(row.userId);
1757
- return row.avatarUrl ? avatarPath(row.identityId) : void 0;
1780
+ return row.avatarUrl ? avatarPath(row.identityId, row.avatarUrl) : void 0;
1758
1781
  }
1759
1782
  async function readBoundedBody(response, maxBytes) {
1760
1783
  if (!response.body) return /* @__PURE__ */ new Uint8Array();
@@ -1818,9 +1841,10 @@ async function fetchAvatarImage(url, provider) {
1818
1841
  };
1819
1842
  }
1820
1843
  /**
1821
- * Read the saved photo of one Identity. Junior fetches it from the provider
1822
- * once and keeps it in the state cache for a day. Return undefined when the
1823
- * Identity has no photo or the provider does not return one.
1844
+ * Read the saved photo of one Identity and its version. Junior fetches it
1845
+ * from the provider once and keeps it in the state cache for a day. Return
1846
+ * undefined when the Identity has no photo or the provider does not return
1847
+ * one.
1824
1848
  */
1825
1849
  async function readAvatarImage(db, state, identityId) {
1826
1850
  const [row] = await db.select({
@@ -1828,15 +1852,19 @@ async function readAvatarImage(db, state, identityId) {
1828
1852
  provider: juniorIdentities.provider
1829
1853
  }).from(juniorIdentities).where(and(eq(juniorIdentities.id, identityId), eq(juniorIdentities.kind, "user"))).limit(1);
1830
1854
  const url = row ? parseAvatarUrl(row.avatarUrl, row.provider) : void 0;
1831
- if (!row || !url) return void 0;
1832
- const key = `avatar:${createHash("sha256").update(url).digest("hex")}`;
1833
- return await createCache(state, "core").getOrLoad(key, async () => {
1855
+ if (!row?.avatarUrl || !url) return void 0;
1856
+ const key = `avatar:${avatarUrlHash(url)}`;
1857
+ const image = await createCache(state, "core").getOrLoad(key, async () => {
1834
1858
  const loaded = await fetchAvatarImage(url, row.provider);
1835
1859
  return {
1836
1860
  value: loaded,
1837
1861
  ttlMs: loaded ? AVATAR_CACHE_TTL_MS : AVATAR_FAILURE_TTL_MS
1838
1862
  };
1839
- }) ?? void 0;
1863
+ });
1864
+ return image ? {
1865
+ image,
1866
+ version: avatarVersion(row.avatarUrl)
1867
+ } : void 0;
1840
1868
  }
1841
1869
  // src/chat/plugins/viewer.ts
1842
1870
  function identityFromRow(row) {
@@ -1950,4 +1978,4 @@ async function readIdentityByIdFromSql(db, identityId) {
1950
1978
  async function readIdentityById(identityId) {
1951
1979
  return await readIdentityByIdFromSql(getDb(), identityId);
1952
1980
  }
1953
- export { startLocationThread as A, upsertLinkedIdentity as B, getSqlExecutor as C, createSqlStore as D, loadConversationSlackTenantIds as E, deleteProviderIdentityForUser as F, agentTurnTotalTokens as G, parseSessionSource as H, findUserByVerifiedEmails as I, resolveRootConversationId as J, agentTurnUsageSchema as K, readUserTimezone as L, resolveActorIdentityId as M, saveActorAvatar as N, withConversationMutationLock as O, deleteProviderIdentityForSlackUser as P, sameDestination as Q, recordGoogleSignIn as R, getDb as S, withConversationEventLock as T, addAgentTurnUsage as U, bindProviderConversation as V, agentTurnCostUsd as W, parseDestination as X, createSlackDestination as Y, requireSlackDestination as Z, closeDb as _, readActorIdentityFromSql as a, getConversationMessageSearchStore as b, readViewerAvatarUrl as c, updateViewerDisplayName as d, identityAvatarUrl as f, createCache as g, readUserAvatarUrls as h, readActorIdentity as i, SYSTEM_IDENTITY_ID as j, locationFromRow as k, resolveViewerUser as l, readAvatarImage as m, findUserByEmailFromSql as n, readIdentityById as o, parseAvatarUrl as p, hasAgentTurnUsage as q, listAdminUsersFromSql as r, readIdentityByIdFromSql as s, findUserByEmail as t, setUserAdminFromSql as u, db_exports as v, createSqlConversationEventStore as w, getConversationStore as x, getConversationEventStore as y, saveUserTimezone as z };
1981
+ export { sameDestination as $, locationFromRow as A, saveUserTimezone as B, getSqlExecutor as C, loadConversationSlackTenantIds as D, withConversationEventLock as E, deleteProviderIdentityForSlackUser as F, agentTurnCostUsd as G, bindProviderConversation as H, deleteProviderIdentityForUser as I, hasAgentTurnUsage as J, agentTurnTotalTokens as K, findUserByVerifiedEmails as L, SYSTEM_IDENTITY_ID as M, resolveActorIdentityId as N, createSqlStore as O, saveActorAvatar as P, requireSlackDestination as Q, readUserTimezone as R, getDb as S, eventFromRow as T, parseSessionSource as U, upsertLinkedIdentity as V, addAgentTurnUsage as W, createSlackDestination as X, resolveRootConversationId as Y, parseDestination as Z, closeDb as _, readActorIdentityFromSql as a, getConversationMessageSearchStore as b, readViewerAvatarUrl as c, updateViewerDisplayName as d, identityAvatarUrl as f, createCache as g, readUserAvatarUrls as h, readActorIdentity as i, startLocationThread as j, withConversationMutationLock as k, resolveViewerUser as l, readAvatarImage as m, findUserByEmailFromSql as n, readIdentityById as o, parseAvatarUrl as p, agentTurnUsageSchema as q, listAdminUsersFromSql as r, readIdentityByIdFromSql as s, findUserByEmail as t, setUserAdminFromSql as u, db_exports as v, createSqlConversationEventStore as w, getConversationStore as x, getConversationEventStore as y, recordGoogleSignIn as z };
@@ -1,14 +1,14 @@
1
- import { Dn as failTurnRecord, Nt as openConversationProjection, On as getTurnRecord, Tn as abandonTurnRecord, dn as PluginCredentialFailureError, hn as AuthorizationFlowDisabledError, mr as isTurnExecutionLimitExceededError, wn as saveTurnCheckpoint, yr as getTerminalAssistantMessages } from "./turn-session-routing-BFVp8TbU.js";
2
- import { n as botConfig } from "./config-DrAeD8sr.js";
3
- import { j as SYSTEM_IDENTITY_ID, x as getConversationStore, y as getConversationEventStore } from "./viewer-D3jhuP66.js";
1
+ import { G as AuthorizationFlowDisabledError, La as loadForkHistory, V as PluginCredentialFailureError, Wt as isTurnExecutionLimitExceededError, Yt as getTerminalAssistantMessages, et as saveTurnCheckpoint, it as getTurnRecord, l as openConversationProjection, rt as failTurnRecord, tt as abandonTurnRecord } from "./projection-BOpi891B.js";
2
+ import { n as botConfig } from "./config-C6FTd7PX.js";
3
+ import { M as SYSTEM_IDENTITY_ID, x as getConversationStore, y as getConversationEventStore } from "./viewer-C6Y1aXjZ.js";
4
4
  import { _ as sameActorIdentity, p as agentInstructionProvenance } from "./media-U6Tuz6Mi.js";
5
- import { Dt as withoutSavedSteeringMessages, Et as buildSteeringPiMessage, Nt as readBlockedReport, gt as stopChildAgentsOf, vt as recordSubagentEnded, yt as recordSubagentStarted } from "./agent-runner-BiHsH8O_.js";
6
- import { E as markAgentInvocationSteered, H as TurnInputCommitLostError, K as isTurnInputCommitLostError, L as agentTypeInstructions, O as reportReplacedAgentInvocation, R as agentTypeStartsWithParentHistory, S as markAgentInvocationMailboxAppended, T as markAgentInvocationRunning, W as getConversationTurnBoundaryError, _ as getLatestAgentInvocationForConversation, c as getAgentInvocation, f as getAgentInvocationMessageId, m as getAgentInvocationTurnId, n as completeAgentInvocation, r as createAgentInvocation, s as getActiveAgentInvocationForConversation, w as markAgentInvocationPaused, y as isTerminalAgentInvocation } from "./store-Di0E4EvU.js";
7
- import { n as getAssistantReplyText } from "./assistant-reply-DgtX9DaJ.js";
8
- import { C as maybePostAutomatedTurnLimitNotice, D as recordFinishedTurnForAutomatedLimit, O as resetAutomatedTurnLimit } from "./session-stop-DuUB1rwQ.js";
9
- import { r as appendAndEnqueueInboundMessage } from "./store-BF2XEeQz.js";
10
- import { t as ConversationTurnLifecycleService } from "./turn-lifecycle-CTLEIEOn.js";
11
- import { a as persistThreadStateById, n as getPersistedSandboxState, r as getPersistedThreadState } from "./thread-state-BQ1T3eeF.js";
5
+ import { Dt as buildSteeringPiMessage, Ot as withoutSavedSteeringMessages, Pt as readBlockedReport, _t as stopChildAgentsOf, bt as recordSubagentStarted, yt as recordSubagentEnded } from "./agent-runner-D7Qz-lGO.js";
6
+ import { B as agentTypeStartsWithParentHistory, E as markAgentInvocationSteered, J as isTurnInputCommitLostError, K as getConversationTurnBoundaryError, O as reportReplacedAgentInvocation, S as markAgentInvocationMailboxAppended, T as markAgentInvocationRunning, W as TurnInputCommitLostError, d as getAgentInvocationMessageId, g as getLatestAgentInvocationForConversation, n as completeAgentInvocation, o as getActiveAgentInvocationForConversation, p as getAgentInvocationTurnId, r as createAgentInvocation, s as getAgentInvocation, v as isTerminalAgentInvocation, w as markAgentInvocationPaused, y as listActiveAgentInvocationsForConversation, z as agentTypeInstructions } from "./store-C1xuZcLd.js";
7
+ import { n as getAssistantReplyText } from "./assistant-reply-wZ7i4OFa.js";
8
+ import { A as recordFinishedTurnForAutomatedLimit, j as resetAutomatedTurnLimit, w as maybePostAutomatedTurnLimitNotice } from "./session-stop-DaT_7tYa.js";
9
+ import { r as appendAndEnqueueInboundMessage } from "./store-DaxUuOmD.js";
10
+ import { t as ConversationTurnLifecycleService } from "./turn-lifecycle-BdPKdPS8.js";
11
+ import { a as persistThreadStateById, n as getPersistedSandboxState, r as getPersistedThreadState } from "./thread-state-BkUpE9Ae.js";
12
12
  import { isDeepStrictEqual } from "node:util";
13
13
  import { z } from "zod";
14
14
  // src/chat/runtime/turn-execution.ts
@@ -56,34 +56,6 @@ async function finishTurn(conversationId, turnId, saved) {
56
56
  outcome: saved.outcome
57
57
  });
58
58
  }
59
- // src/chat/agent-invocations/fork-history.ts
60
- /**
61
- * Cut parent history for a fork: everything before the parent's message that
62
- * started the fork, through the last point where every tool call has its
63
- * result. A model request with an unanswered tool call fails, and the parent
64
- * can commit its history in the middle of a tool round.
65
- */
66
- function forkHistoryFrom(messages, parentToolCallId) {
67
- const spawnIndex = parentToolCallId ? messages.findIndex((message) => message.role === "assistant" && message.content.some((part) => part.type === "toolCall" && part.id === parentToolCallId)) : -1;
68
- const before = spawnIndex >= 0 ? messages.slice(0, spawnIndex) : messages;
69
- const pending = /* @__PURE__ */ new Set();
70
- let end = 0;
71
- for (const [index, message] of before.entries()) {
72
- if (message.role === "assistant") {
73
- for (const part of message.content) if (part.type === "toolCall") pending.add(part.id);
74
- } else if (message.role === "toolResult") pending.delete(message.toolCallId);
75
- if (pending.size === 0) end = index + 1;
76
- }
77
- return before.slice(0, end);
78
- }
79
- /**
80
- * Load the start history of a fork child from its parent Conversation. The
81
- * child commits it as its own history in its first checkpoint, so later
82
- * agent invocations of the child read it from the child Conversation.
83
- */
84
- async function loadForkHistory(invocation) {
85
- return forkHistoryFrom((await openConversationProjection({ conversationId: invocation.parentConversationId })).messages, invocation.parentToolCallId);
86
- }
87
59
  // src/chat/agent-invocations/work.ts
88
60
  const agentInvocationMailboxMetadataSchema = z.object({
89
61
  agentInvocationId: z.string().min(1),
@@ -264,7 +236,7 @@ async function persistTerminalLifecycle(terminal) {
264
236
  await finishTurn(terminal.childConversationId, turnId, agentInvocationTurnEnd(terminal));
265
237
  }
266
238
  /** Stop an invocation whose worker died, or project its terminal turn result. */
267
- async function projectTerminalTurn(invocation) {
239
+ async function projectTerminalTurn(invocation, options = {}) {
268
240
  let turn = await getTurnRecord(invocation.childConversationId, getAgentInvocationTurnId(invocation.invocationId));
269
241
  if (!turn) return;
270
242
  if (turn.state === "running") {
@@ -281,6 +253,7 @@ async function projectTerminalTurn(invocation) {
281
253
  const failed = turn.state !== "completed" || Boolean(turn.errorMessage);
282
254
  const blocked = turn.turnStartMessageIndex === void 0 ? void 0 : readBlockedReport(turn.piMessages.slice(turn.turnStartMessageIndex));
283
255
  return await completeAgentInvocation({
256
+ ...options,
284
257
  invocationId: invocation.invocationId,
285
258
  ...failed ? {
286
259
  errorMessage: turn.errorMessage ?? "Agent invocation failed",
@@ -294,6 +267,20 @@ async function projectTerminalTurn(invocation) {
294
267
  }
295
268
  });
296
269
  }
270
+ /**
271
+ * End an agent invocation that is still running or paused while other work
272
+ * of its child starts. A saved finished Turn gives its result. Otherwise it
273
+ * fails. The newer work does not continue this task, so the result goes to
274
+ * the parent on its own.
275
+ */
276
+ async function endOtherActiveAgentInvocation(invocation) {
277
+ return await projectTerminalTurn(invocation, { reportNow: true }) ?? await completeAgentInvocation({
278
+ invocationId: invocation.invocationId,
279
+ errorMessage: "This task was not resumed. Newer work for this child agent started, so this task ended without a result.",
280
+ reportNow: true,
281
+ status: "failed"
282
+ });
283
+ }
297
284
  /** Classify authorization failures that terminally block background work. */
298
285
  function blockingInvocationError(error) {
299
286
  const cause = getConversationTurnBoundaryError(error)?.cause ?? error;
@@ -428,6 +415,7 @@ function createAgentInvocationWorker(agentRunner, options = {}) {
428
415
  await acknowledge();
429
416
  return { status: "completed" };
430
417
  }
418
+ for (const active of await listActiveAgentInvocationsForConversation(invocation.childConversationId)) if (active.invocationId !== invocation.invocationId) await finish(await endOtherActiveAgentInvocation(active));
431
419
  invocation = await markAgentInvocationRunning(invocation.invocationId) ?? invocation;
432
420
  if (isTerminalAgentInvocation(invocation)) {
433
421
  await finish(invocation);
@@ -1,10 +1,10 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.js";
2
- import { Xn as isInvalidConversationRecordError, Yn as isFinalAttempt, qn as hasConversationStop } from "./turn-session-routing-BFVp8TbU.js";
3
- import { y as isProviderRetryError } from "./client-B0jlQSgO.js";
4
- import { i as getChatConfig } from "./config-DrAeD8sr.js";
5
- import { T as withLogContext, b as setSpanAttributes, h as logWarn, m as logInfo, p as logException } from "./logging-DuwXUuIY.js";
6
- import { f as getTurnRequestDeadline, m as ConversationQueueMessageRejectedError } from "./job-queue-CEeLYkSj.js";
7
- import { C as requestAnotherSlice, T as startConversationWork, b as recordConversationRetry, d as countPendingConversationMessages, f as deadLetterAttempt, g as getConversationWorkState, i as beginConversationResume, l as completeConversationStop, m as ensureConversationWake, o as checkInConversationWork, p as drainConversationMailbox, s as clearConsumedConversationWake, t as ackMessages, u as completeConversationWork, x as releaseConversationWork, y as recordAttemptFailure } from "./store-BF2XEeQz.js";
2
+ import { Ct as hasConversationStop, Et as isInvalidConversationRecordError, Tt as isFinalAttempt } from "./projection-BOpi891B.js";
3
+ import { b as isProviderRetryError } from "./client-DhqbV0Fe.js";
4
+ import { i as getChatConfig } from "./config-C6FTd7PX.js";
5
+ import { T as withLogContext, b as setSpanAttributes, h as logWarn, m as logInfo, p as logException } from "./logging-CNs1e04n.js";
6
+ import { f as getTurnRequestDeadline, m as ConversationQueueMessageRejectedError } from "./job-queue-C4lphW9Y.js";
7
+ import { C as requestAnotherSlice, T as startConversationWork, b as recordConversationRetry, d as countPendingConversationMessages, f as deadLetterAttempt, g as getConversationWorkState, i as beginConversationResume, l as completeConversationStop, m as ensureConversationWake, o as checkInConversationWork, p as drainConversationMailbox, s as clearConsumedConversationWake, t as ackMessages, u as completeConversationWork, x as releaseConversationWork, y as recordAttemptFailure } from "./store-DaxUuOmD.js";
8
8
  // src/chat/task-execution/attempt-selection.ts
9
9
  function selectContiguousTurnBatch(messages) {
10
10
  const first = messages[0];
@@ -0,0 +1,10 @@
1
+ CREATE TABLE "junior_conversation_read_positions" (
2
+ "user_id" text NOT NULL,
3
+ "conversation_id" text NOT NULL,
4
+ "last_read_seq" integer NOT NULL,
5
+ "updated_at" timestamp with time zone NOT NULL,
6
+ CONSTRAINT "junior_conversation_read_positions_user_conversation_pk" PRIMARY KEY("user_id","conversation_id")
7
+ );
8
+ --> statement-breakpoint
9
+ ALTER TABLE "junior_conversation_read_positions" ADD CONSTRAINT "junior_conversation_read_positions_user_id_junior_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."junior_users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
10
+ ALTER TABLE "junior_conversation_read_positions" ADD CONSTRAINT "junior_conversation_read_positions_conversation_id_junior_conversations_conversation_id_fk" FOREIGN KEY ("conversation_id") REFERENCES "public"."junior_conversations"("conversation_id") ON DELETE no action ON UPDATE no action;