@sentry/junior 0.84.1 → 0.86.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.
- package/dist/{agent-hooks-TFPY6V3T.js → agent-hooks-NWOUB3UR.js} +7 -7
- package/dist/app.js +916 -363
- package/dist/chat/conversation-privacy.d.ts +12 -2
- package/dist/chat/conversations/sql/migrations.d.ts +1 -1
- package/dist/chat/conversations/sql/store.d.ts +9 -1
- package/dist/chat/conversations/store.d.ts +13 -0
- package/dist/chat/current-instruction.d.ts +6 -0
- package/dist/chat/ingress/message-changed.d.ts +2 -0
- package/dist/chat/respond-helpers.d.ts +1 -2
- package/dist/chat/respond.d.ts +7 -1
- package/dist/chat/runtime/reply-executor.d.ts +2 -10
- package/dist/chat/runtime/slack-runtime.d.ts +5 -4
- package/dist/chat/runtime/turn.d.ts +7 -0
- package/dist/chat/services/persist-retry.d.ts +2 -0
- package/dist/chat/services/turn-session-record.d.ts +31 -2
- package/dist/chat/slack/client.d.ts +33 -2
- package/dist/chat/slack/conversation-context.d.ts +15 -0
- package/dist/chat/state/turn-session.d.ts +9 -0
- package/dist/chat/task-execution/queue.d.ts +1 -1
- package/dist/chat/task-execution/slack-work.d.ts +10 -2
- package/dist/chat/task-execution/state.d.ts +44 -4
- package/dist/chat/task-execution/store.d.ts +21 -4
- package/dist/chat/task-execution/worker.d.ts +11 -4
- package/dist/chat/tools/slack/channel-access.d.ts +29 -0
- package/dist/chat/tools/slack/thread-read.d.ts +4 -1
- package/dist/{chunk-AL6ZFV7U.js → chunk-2NFV5FMB.js} +4 -4
- package/dist/{chunk-JK7376UT.js → chunk-37B2R2QJ.js} +17 -17
- package/dist/{chunk-QWKB6NJV.js → chunk-6I6HBOQM.js} +80 -15
- package/dist/{chunk-SBYMRDH7.js → chunk-6O5UI3RG.js} +1 -1
- package/dist/{chunk-CWMMGUWM.js → chunk-BRSQQRG6.js} +1 -1
- package/dist/{chunk-FPN7NYTE.js → chunk-ENPSU7L7.js} +165 -27
- package/dist/{chunk-T2YRJZ5A.js → chunk-FPHA6GCQ.js} +905 -711
- package/dist/{chunk-MJ4E2PK7.js → chunk-GB5DFM4D.js} +1 -1
- package/dist/{chunk-BAPRSWNW.js → chunk-GGD6WK6V.js} +124 -25
- package/dist/{chunk-SVFA43LT.js → chunk-JRXCSSSU.js} +19 -4
- package/dist/{chunk-DR75T7J3.js → chunk-L7OHKDOX.js} +9 -5
- package/dist/{chunk-LSG6PQF4.js → chunk-RIB3M6YA.js} +2 -2
- package/dist/{chunk-ALL7GG6U.js → chunk-ZU2ALUVQ.js} +45 -6
- package/dist/cli/chat.js +4 -4
- package/dist/cli/plugins.js +7 -7
- package/dist/cli/snapshot-warmup.js +4 -4
- package/dist/cli/upgrade.js +10 -7
- package/dist/{db-ELRCNB4A.js → db-SZVUU7RB.js} +4 -4
- package/dist/handlers/github-webhook/issue-comment.d.ts +3 -0
- package/dist/handlers/github-webhook/pull-request-review-comment.d.ts +3 -0
- package/dist/instrumentation.js +1 -1
- package/dist/nitro.js +4 -4
- package/dist/reporting/conversations.d.ts +2 -1
- package/dist/reporting.js +110 -34
- package/dist/{runner-2XU6QQND.js → runner-HEBRPNN2.js} +27 -13
- package/package.json +5 -5
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
isSnapshotMissingError,
|
|
11
11
|
resolveRuntimeDependencySnapshot,
|
|
12
12
|
runNonInteractiveCommand
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-RIB3M6YA.js";
|
|
14
14
|
import {
|
|
15
15
|
pluginTaskId,
|
|
16
16
|
pluginTaskParamsSchema,
|
|
@@ -22,8 +22,8 @@ import {
|
|
|
22
22
|
buildPluginSystemPromptContributions,
|
|
23
23
|
buildSystemPrompt,
|
|
24
24
|
buildTurnContextPrompt,
|
|
25
|
-
escapeXml,
|
|
26
25
|
getAgentTurnSessionRecord,
|
|
26
|
+
getInterruptionMarker,
|
|
27
27
|
loadConnectedMcpProviders,
|
|
28
28
|
recordAuthorizationRequested,
|
|
29
29
|
recordMcpProviderConnected,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
recordSubagentStarted,
|
|
32
32
|
recordToolExecutionStarted,
|
|
33
33
|
upsertAgentTurnSessionRecord
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-37B2R2QJ.js";
|
|
35
35
|
import {
|
|
36
36
|
createPluginEmbedder,
|
|
37
37
|
createPluginHookRunner,
|
|
@@ -42,14 +42,15 @@ import {
|
|
|
42
42
|
getPlugins,
|
|
43
43
|
getSlackToolContext,
|
|
44
44
|
resolveChannelCapabilities
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-2NFV5FMB.js";
|
|
46
46
|
import {
|
|
47
47
|
createPluginLogger,
|
|
48
48
|
createPluginState
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-BRSQQRG6.js";
|
|
50
50
|
import {
|
|
51
|
+
getConversationStore,
|
|
51
52
|
getDb
|
|
52
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-6I6HBOQM.js";
|
|
53
54
|
import {
|
|
54
55
|
SANDBOX_DATA_ROOT,
|
|
55
56
|
SANDBOX_SKILLS_ROOT,
|
|
@@ -60,7 +61,7 @@ import {
|
|
|
60
61
|
import {
|
|
61
62
|
acquireActiveLock,
|
|
62
63
|
getStateAdapter
|
|
63
|
-
} from "./chunk-
|
|
64
|
+
} from "./chunk-GB5DFM4D.js";
|
|
64
65
|
import {
|
|
65
66
|
SlackActionError,
|
|
66
67
|
downloadPrivateSlackFile,
|
|
@@ -71,7 +72,7 @@ import {
|
|
|
71
72
|
normalizeSlackConversationId,
|
|
72
73
|
parseDestination,
|
|
73
74
|
withSlackRetries
|
|
74
|
-
} from "./chunk-
|
|
75
|
+
} from "./chunk-GGD6WK6V.js";
|
|
75
76
|
import {
|
|
76
77
|
GEN_AI_PROVIDER_NAME,
|
|
77
78
|
GEN_AI_SERVER_ADDRESS,
|
|
@@ -82,7 +83,9 @@ import {
|
|
|
82
83
|
completeObject,
|
|
83
84
|
completeText,
|
|
84
85
|
encodeNonImageAttachmentForPrompt,
|
|
86
|
+
escapeXml,
|
|
85
87
|
extractAssistantText,
|
|
88
|
+
extractCurrentInstructionBody,
|
|
86
89
|
getGatewayApiKey,
|
|
87
90
|
getPiGatewayApiKey,
|
|
88
91
|
getPiMessageRole,
|
|
@@ -98,13 +101,14 @@ import {
|
|
|
98
101
|
nextProviderRetry,
|
|
99
102
|
normalizeSlackEmojiName,
|
|
100
103
|
normalizeToolNameFromResult,
|
|
104
|
+
renderCurrentInstruction,
|
|
101
105
|
resolveGatewayModel,
|
|
102
106
|
stripRuntimeTurnContext,
|
|
103
107
|
summarizeMessageText,
|
|
104
108
|
toObservablePromptPart,
|
|
105
109
|
trimTrailingAssistantMessages,
|
|
106
110
|
upsertActiveSkill
|
|
107
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-ZU2ALUVQ.js";
|
|
108
112
|
import {
|
|
109
113
|
parseSlackThreadId,
|
|
110
114
|
resolveConversationPrivacy,
|
|
@@ -114,7 +118,7 @@ import {
|
|
|
114
118
|
toGenAiPayloadMetadata,
|
|
115
119
|
toGenAiPayloadTraceAttributes,
|
|
116
120
|
toGenAiTextMetadata
|
|
117
|
-
} from "./chunk-
|
|
121
|
+
} from "./chunk-L7OHKDOX.js";
|
|
118
122
|
import {
|
|
119
123
|
discoverSkills,
|
|
120
124
|
findSkillByName,
|
|
@@ -136,6 +140,7 @@ import {
|
|
|
136
140
|
listReferenceFiles
|
|
137
141
|
} from "./chunk-6APU57E6.js";
|
|
138
142
|
import {
|
|
143
|
+
buildTurnFailureResponse,
|
|
139
144
|
extractGenAiUsageAttributes,
|
|
140
145
|
extractGenAiUsageSummary,
|
|
141
146
|
getActiveTraceId,
|
|
@@ -648,6 +653,16 @@ var TurnInputCommitLostError = class extends Error {
|
|
|
648
653
|
function isTurnInputCommitLostError(error) {
|
|
649
654
|
return error instanceof TurnInputCommitLostError;
|
|
650
655
|
}
|
|
656
|
+
var TurnInputDeferredError = class extends Error {
|
|
657
|
+
code = "turn_input_deferred";
|
|
658
|
+
constructor(message = "Turn input is deferred until the active resume ends") {
|
|
659
|
+
super(message);
|
|
660
|
+
this.name = "TurnInputDeferredError";
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
function isTurnInputDeferredError(error) {
|
|
664
|
+
return error instanceof TurnInputDeferredError;
|
|
665
|
+
}
|
|
651
666
|
function startActiveTurn(args) {
|
|
652
667
|
args.conversation.processing.activeTurnId = args.nextTurnId;
|
|
653
668
|
args.updateConversationStats(args.conversation);
|
|
@@ -677,184 +692,592 @@ function markTurnFailed(args) {
|
|
|
677
692
|
args.updateConversationStats(args.conversation);
|
|
678
693
|
}
|
|
679
694
|
|
|
680
|
-
// src/chat/
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
695
|
+
// src/chat/usage.ts
|
|
696
|
+
var COMPONENT_USAGE_FIELDS = [
|
|
697
|
+
"inputTokens",
|
|
698
|
+
"outputTokens",
|
|
699
|
+
"cachedInputTokens",
|
|
700
|
+
"cacheCreationTokens"
|
|
701
|
+
];
|
|
702
|
+
function hasAgentTurnUsage(usage) {
|
|
703
|
+
return Boolean(
|
|
704
|
+
usage && Object.values(usage).some(
|
|
705
|
+
(value) => typeof value === "number" && Number.isFinite(value)
|
|
706
|
+
)
|
|
707
|
+
);
|
|
708
|
+
}
|
|
709
|
+
function getFiniteCount(value) {
|
|
710
|
+
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : void 0;
|
|
711
|
+
}
|
|
712
|
+
function getComponentTotal(usage) {
|
|
713
|
+
let total;
|
|
714
|
+
for (const field of COMPONENT_USAGE_FIELDS) {
|
|
715
|
+
const value = getFiniteCount(usage[field]);
|
|
716
|
+
if (value === void 0) continue;
|
|
717
|
+
total = (total ?? 0) + value;
|
|
718
|
+
}
|
|
719
|
+
return total;
|
|
720
|
+
}
|
|
721
|
+
function addAgentTurnUsage(...usages) {
|
|
722
|
+
const components = {};
|
|
723
|
+
let componentTotal;
|
|
724
|
+
let totalOnlyTokens;
|
|
725
|
+
for (const usage of usages) {
|
|
726
|
+
if (!usage) continue;
|
|
727
|
+
const usageComponentTotal = getComponentTotal(usage);
|
|
728
|
+
if (usageComponentTotal !== void 0) {
|
|
729
|
+
componentTotal = (componentTotal ?? 0) + usageComponentTotal;
|
|
730
|
+
for (const field of COMPONENT_USAGE_FIELDS) {
|
|
731
|
+
const value = getFiniteCount(usage[field]);
|
|
732
|
+
if (value === void 0) continue;
|
|
733
|
+
components[field] = (components[field] ?? 0) + value;
|
|
697
734
|
}
|
|
698
|
-
|
|
735
|
+
continue;
|
|
736
|
+
}
|
|
737
|
+
const totalTokens = getFiniteCount(usage.totalTokens);
|
|
738
|
+
if (totalTokens !== void 0) {
|
|
739
|
+
totalOnlyTokens = (totalOnlyTokens ?? 0) + totalTokens;
|
|
699
740
|
}
|
|
700
741
|
}
|
|
701
|
-
|
|
702
|
-
|
|
742
|
+
if (totalOnlyTokens !== void 0) {
|
|
743
|
+
return {
|
|
744
|
+
totalTokens: totalOnlyTokens + (componentTotal ?? 0)
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
return hasAgentTurnUsage(components) ? components : void 0;
|
|
703
748
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
...new Set(providers.flatMap((p) => p.configKeys))
|
|
712
|
-
].sort();
|
|
713
|
-
logInfo(
|
|
714
|
-
"capability_catalog_loaded",
|
|
715
|
-
{},
|
|
749
|
+
|
|
750
|
+
// src/chat/services/turn-session-record.ts
|
|
751
|
+
var AGENT_CONTINUE_MAX_SLICES = 48;
|
|
752
|
+
function logSessionRecordError(error, eventName, args, attributes, message) {
|
|
753
|
+
logException(
|
|
754
|
+
error,
|
|
755
|
+
eventName,
|
|
716
756
|
{
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
757
|
+
slackThreadId: args.logContext.threadId,
|
|
758
|
+
slackUserId: args.logContext.requesterId,
|
|
759
|
+
slackChannelId: args.logContext.channelId,
|
|
760
|
+
runId: args.logContext.runId,
|
|
761
|
+
assistantUserName: args.logContext.assistantUserName,
|
|
762
|
+
modelId: args.logContext.modelId
|
|
722
763
|
},
|
|
723
|
-
|
|
764
|
+
{
|
|
765
|
+
"app.ai.resume_conversation_id": args.conversationId,
|
|
766
|
+
"app.ai.resume_session_id": args.sessionId,
|
|
767
|
+
...attributes
|
|
768
|
+
},
|
|
769
|
+
message
|
|
724
770
|
);
|
|
725
771
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
772
|
+
function addDurationMs(prior, current) {
|
|
773
|
+
const total = [prior, current].reduce((sum, value) => {
|
|
774
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
775
|
+
return sum;
|
|
776
|
+
}
|
|
777
|
+
return (sum ?? 0) + Math.max(0, Math.floor(value));
|
|
778
|
+
}, void 0);
|
|
779
|
+
return total;
|
|
780
|
+
}
|
|
781
|
+
function isContinuableBoundary(messages) {
|
|
782
|
+
const lastRole = getPiMessageRole(messages.at(-1));
|
|
783
|
+
return lastRole === "user" || lastRole === "toolResult";
|
|
784
|
+
}
|
|
785
|
+
function resumableBoundary(messages, fallbackMessages) {
|
|
786
|
+
const current = trimTrailingAssistantMessages(messages);
|
|
787
|
+
if (current.length > 0 && isContinuableBoundary(current)) {
|
|
788
|
+
return current;
|
|
732
789
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
790
|
+
return trimTrailingAssistantMessages(fallbackMessages ?? []);
|
|
791
|
+
}
|
|
792
|
+
async function loadTurnSessionRecord(ctx) {
|
|
793
|
+
const canUseTurnSession = Boolean(ctx.conversationId && ctx.sessionId);
|
|
794
|
+
const existingSessionRecord = canUseTurnSession && ctx.conversationId && ctx.sessionId ? await getAgentTurnSessionRecord(ctx.conversationId, ctx.sessionId) : void 0;
|
|
795
|
+
const hasAwaitingResumeRecord = Boolean(
|
|
796
|
+
existingSessionRecord && existingSessionRecord.state === "awaiting_resume"
|
|
797
|
+
);
|
|
798
|
+
return {
|
|
799
|
+
canUseTurnSession,
|
|
800
|
+
resumedFromSessionRecord: hasAwaitingResumeRecord,
|
|
801
|
+
currentSliceId: hasAwaitingResumeRecord ? existingSessionRecord.sliceId : 1,
|
|
802
|
+
existingSessionRecord
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
async function persistRunningSessionRecord(args) {
|
|
806
|
+
if (args.messages.length === 0 || !isContinuableBoundary(args.messages)) {
|
|
807
|
+
return false;
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
811
|
+
args.conversationId,
|
|
812
|
+
args.sessionId
|
|
813
|
+
);
|
|
814
|
+
await upsertAgentTurnSessionRecord({
|
|
815
|
+
...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
|
|
816
|
+
conversationId: args.conversationId,
|
|
817
|
+
cumulativeDurationMs: latestSessionRecord?.cumulativeDurationMs,
|
|
818
|
+
cumulativeUsage: latestSessionRecord?.cumulativeUsage,
|
|
819
|
+
...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
|
|
820
|
+
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
821
|
+
sessionId: args.sessionId,
|
|
822
|
+
sliceId: args.sliceId,
|
|
823
|
+
state: "running",
|
|
824
|
+
piMessages: args.messages,
|
|
825
|
+
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
826
|
+
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
827
|
+
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
828
|
+
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {},
|
|
829
|
+
...(args.turnStartMessageIndex ?? latestSessionRecord?.turnStartMessageIndex) !== void 0 ? {
|
|
830
|
+
turnStartMessageIndex: args.turnStartMessageIndex ?? latestSessionRecord?.turnStartMessageIndex
|
|
831
|
+
} : {}
|
|
832
|
+
});
|
|
833
|
+
return true;
|
|
834
|
+
} catch (recordError) {
|
|
835
|
+
logSessionRecordError(
|
|
836
|
+
recordError,
|
|
837
|
+
"agent_turn_running_session_record_failed",
|
|
838
|
+
args,
|
|
839
|
+
{
|
|
840
|
+
"app.ai.resume_slice_id": args.sliceId
|
|
841
|
+
},
|
|
842
|
+
"Failed to persist running turn session record"
|
|
843
|
+
);
|
|
844
|
+
return false;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
async function persistCompletedSessionRecord(args) {
|
|
848
|
+
let sliceId = args.sliceId;
|
|
849
|
+
try {
|
|
850
|
+
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
851
|
+
args.conversationId,
|
|
852
|
+
args.sessionId
|
|
853
|
+
);
|
|
854
|
+
sliceId = sliceId ?? latestSessionRecord?.sliceId;
|
|
855
|
+
if (sliceId === void 0) {
|
|
736
856
|
throw new Error(
|
|
737
|
-
|
|
857
|
+
"Completed session record requires a slice id from the caller or the latest stored record"
|
|
738
858
|
);
|
|
739
859
|
}
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
860
|
+
await upsertAgentTurnSessionRecord({
|
|
861
|
+
...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
|
|
862
|
+
conversationId: args.conversationId,
|
|
863
|
+
cumulativeDurationMs: addDurationMs(
|
|
864
|
+
latestSessionRecord?.cumulativeDurationMs,
|
|
865
|
+
args.currentDurationMs
|
|
866
|
+
),
|
|
867
|
+
cumulativeUsage: addAgentTurnUsage(
|
|
868
|
+
latestSessionRecord?.cumulativeUsage,
|
|
869
|
+
args.currentUsage
|
|
870
|
+
),
|
|
871
|
+
...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
|
|
872
|
+
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
873
|
+
...args.destinationVisibility ? { destinationVisibility: args.destinationVisibility } : {},
|
|
874
|
+
sessionId: args.sessionId,
|
|
875
|
+
sliceId,
|
|
876
|
+
state: "completed",
|
|
877
|
+
piMessages: args.allMessages,
|
|
878
|
+
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
879
|
+
...args.loadedSkillNames ?? latestSessionRecord?.loadedSkillNames ? {
|
|
880
|
+
loadedSkillNames: args.loadedSkillNames ?? latestSessionRecord?.loadedSkillNames
|
|
881
|
+
} : {},
|
|
882
|
+
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
883
|
+
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {},
|
|
884
|
+
...(args.turnStartMessageIndex ?? latestSessionRecord?.turnStartMessageIndex) !== void 0 ? {
|
|
885
|
+
turnStartMessageIndex: args.turnStartMessageIndex ?? latestSessionRecord?.turnStartMessageIndex
|
|
886
|
+
} : {}
|
|
743
887
|
});
|
|
888
|
+
} catch (recordError) {
|
|
889
|
+
logSessionRecordError(
|
|
890
|
+
recordError,
|
|
891
|
+
"agent_turn_completed_session_record_failed",
|
|
892
|
+
args,
|
|
893
|
+
sliceId !== void 0 ? { "app.ai.resume_slice_id": sliceId } : {},
|
|
894
|
+
"Failed to persist completed turn session record"
|
|
895
|
+
);
|
|
744
896
|
}
|
|
745
|
-
};
|
|
746
|
-
|
|
747
|
-
// src/chat/credentials/user-token-store.ts
|
|
748
|
-
import {
|
|
749
|
-
pluginStoredTokensSchema
|
|
750
|
-
} from "@sentry/junior-plugin-api";
|
|
751
|
-
var storedTokensSchema = pluginStoredTokensSchema;
|
|
752
|
-
|
|
753
|
-
// src/chat/credentials/state-adapter-token-store.ts
|
|
754
|
-
var KEY_PREFIX = "oauth-token";
|
|
755
|
-
var BUFFER_MS = 24 * 60 * 60 * 1e3;
|
|
756
|
-
var LONG_LIVED_TTL_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
757
|
-
var REFRESH_LOCK_WAIT_MS = 3e4;
|
|
758
|
-
var REFRESH_LOCK_RETRY_MS = 100;
|
|
759
|
-
function tokenKey(userId, provider) {
|
|
760
|
-
return `${KEY_PREFIX}:${userId}:${provider}`;
|
|
761
897
|
}
|
|
762
|
-
function
|
|
763
|
-
|
|
898
|
+
async function completeDeliveredTurn(args) {
|
|
899
|
+
await persistCompletedSessionRecord({
|
|
900
|
+
channelName: args.channelName,
|
|
901
|
+
conversationId: args.conversationId,
|
|
902
|
+
currentDurationMs: args.durationMs,
|
|
903
|
+
currentUsage: args.usage,
|
|
904
|
+
destination: args.destination,
|
|
905
|
+
destinationVisibility: args.destinationVisibility,
|
|
906
|
+
source: args.source,
|
|
907
|
+
sessionId: args.sessionId,
|
|
908
|
+
sliceId: args.sliceId,
|
|
909
|
+
allMessages: args.messages,
|
|
910
|
+
loadedSkillNames: args.loadedSkillNames,
|
|
911
|
+
logContext: args.logContext,
|
|
912
|
+
requester: args.requester,
|
|
913
|
+
surface: args.surface,
|
|
914
|
+
turnStartMessageIndex: args.turnStartMessageIndex
|
|
915
|
+
});
|
|
764
916
|
}
|
|
765
|
-
async function
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
const parsed = storedTokensSchema.parse(tokens);
|
|
779
|
-
const expiresAt = parsed.refreshTokenExpiresAt ?? parsed.expiresAt;
|
|
780
|
-
const ttlMs2 = expiresAt ? Math.max(expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
|
|
781
|
-
await this.state.set(tokenKey(userId, provider), parsed, ttlMs2);
|
|
782
|
-
}
|
|
783
|
-
async delete(userId, provider) {
|
|
784
|
-
await this.state.delete(tokenKey(userId, provider));
|
|
785
|
-
}
|
|
786
|
-
/** Wait for the per-slot refresh gate so rotated refresh tokens are used once. */
|
|
787
|
-
async withRefresh(userId, provider, callback) {
|
|
788
|
-
const lockKey = refreshLockKey(userId, provider);
|
|
789
|
-
const deadline = Date.now() + REFRESH_LOCK_WAIT_MS;
|
|
790
|
-
while (true) {
|
|
791
|
-
const lock = await acquireActiveLock(this.state, lockKey);
|
|
792
|
-
if (lock) {
|
|
793
|
-
try {
|
|
794
|
-
return await callback();
|
|
795
|
-
} finally {
|
|
796
|
-
await this.state.releaseLock(lock);
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
if (Date.now() >= deadline) {
|
|
800
|
-
throw new Error(`Could not acquire OAuth token refresh lock`);
|
|
801
|
-
}
|
|
802
|
-
await sleep(REFRESH_LOCK_RETRY_MS);
|
|
917
|
+
async function persistAuthPauseSessionRecord(args) {
|
|
918
|
+
const nextSliceId = args.currentSliceId + 1;
|
|
919
|
+
try {
|
|
920
|
+
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
921
|
+
args.conversationId,
|
|
922
|
+
args.sessionId
|
|
923
|
+
);
|
|
924
|
+
const piMessages = resumableBoundary(
|
|
925
|
+
args.messages,
|
|
926
|
+
latestSessionRecord?.piMessages
|
|
927
|
+
);
|
|
928
|
+
if (piMessages.length > 0 && !isContinuableBoundary(piMessages)) {
|
|
929
|
+
return void 0;
|
|
803
930
|
}
|
|
931
|
+
return await upsertAgentTurnSessionRecord({
|
|
932
|
+
...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
|
|
933
|
+
conversationId: args.conversationId,
|
|
934
|
+
cumulativeDurationMs: addDurationMs(
|
|
935
|
+
latestSessionRecord?.cumulativeDurationMs,
|
|
936
|
+
args.currentDurationMs
|
|
937
|
+
),
|
|
938
|
+
cumulativeUsage: addAgentTurnUsage(
|
|
939
|
+
latestSessionRecord?.cumulativeUsage,
|
|
940
|
+
args.currentUsage
|
|
941
|
+
),
|
|
942
|
+
...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
|
|
943
|
+
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
944
|
+
sessionId: args.sessionId,
|
|
945
|
+
sliceId: nextSliceId,
|
|
946
|
+
state: "awaiting_resume",
|
|
947
|
+
piMessages,
|
|
948
|
+
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
949
|
+
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
950
|
+
resumeReason: "auth",
|
|
951
|
+
resumedFromSliceId: args.currentSliceId,
|
|
952
|
+
errorMessage: args.errorMessage,
|
|
953
|
+
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
954
|
+
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {}
|
|
955
|
+
});
|
|
956
|
+
} catch (recordError) {
|
|
957
|
+
logSessionRecordError(
|
|
958
|
+
recordError,
|
|
959
|
+
"agent_turn_auth_resume_session_record_failed",
|
|
960
|
+
args,
|
|
961
|
+
{
|
|
962
|
+
"app.ai.resume_from_slice_id": args.currentSliceId,
|
|
963
|
+
"app.ai.resume_next_slice_id": nextSliceId
|
|
964
|
+
},
|
|
965
|
+
"Failed to persist auth session record before retry"
|
|
966
|
+
);
|
|
804
967
|
}
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
// src/chat/capabilities/factory.ts
|
|
808
|
-
var sandboxEgressRouters = /* @__PURE__ */ new WeakMap();
|
|
809
|
-
function createUserTokenStore() {
|
|
810
|
-
return new StateAdapterTokenStore(getStateAdapter());
|
|
968
|
+
return void 0;
|
|
811
969
|
}
|
|
812
|
-
function
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
970
|
+
async function persistTimeoutSessionRecord(args) {
|
|
971
|
+
const nextSliceId = args.currentSliceId + 1;
|
|
972
|
+
try {
|
|
973
|
+
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
974
|
+
args.conversationId,
|
|
975
|
+
args.sessionId
|
|
976
|
+
);
|
|
977
|
+
const piMessages = resumableBoundary(
|
|
978
|
+
args.messages,
|
|
979
|
+
latestSessionRecord?.piMessages
|
|
980
|
+
);
|
|
981
|
+
if (piMessages.length === 0 || !isContinuableBoundary(piMessages)) {
|
|
982
|
+
return void 0;
|
|
819
983
|
}
|
|
820
|
-
|
|
821
|
-
|
|
984
|
+
const cumulativeDurationMs = addDurationMs(
|
|
985
|
+
latestSessionRecord?.cumulativeDurationMs,
|
|
986
|
+
args.currentDurationMs
|
|
987
|
+
);
|
|
988
|
+
const cumulativeUsage = addAgentTurnUsage(
|
|
989
|
+
latestSessionRecord?.cumulativeUsage,
|
|
990
|
+
args.currentUsage
|
|
991
|
+
);
|
|
992
|
+
if (nextSliceId > AGENT_CONTINUE_MAX_SLICES) {
|
|
993
|
+
return await upsertAgentTurnSessionRecord({
|
|
994
|
+
...args.channelName ?? latestSessionRecord?.channelName ? {
|
|
995
|
+
channelName: args.channelName ?? latestSessionRecord?.channelName
|
|
996
|
+
} : {},
|
|
997
|
+
conversationId: args.conversationId,
|
|
998
|
+
cumulativeDurationMs,
|
|
999
|
+
cumulativeUsage,
|
|
1000
|
+
...args.destination ?? latestSessionRecord?.destination ? {
|
|
1001
|
+
destination: args.destination ?? latestSessionRecord?.destination
|
|
1002
|
+
} : {},
|
|
1003
|
+
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
1004
|
+
sessionId: args.sessionId,
|
|
1005
|
+
sliceId: args.currentSliceId,
|
|
1006
|
+
state: "failed",
|
|
1007
|
+
piMessages,
|
|
1008
|
+
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
1009
|
+
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
1010
|
+
resumeReason: "timeout",
|
|
1011
|
+
resumedFromSliceId: latestSessionRecord?.resumedFromSliceId,
|
|
1012
|
+
errorMessage: `Agent continuation exceeded slice limit (${AGENT_CONTINUE_MAX_SLICES})`,
|
|
1013
|
+
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
1014
|
+
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {}
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
return await upsertAgentTurnSessionRecord({
|
|
1018
|
+
...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
|
|
1019
|
+
conversationId: args.conversationId,
|
|
1020
|
+
cumulativeDurationMs,
|
|
1021
|
+
cumulativeUsage,
|
|
1022
|
+
...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
|
|
1023
|
+
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
1024
|
+
sessionId: args.sessionId,
|
|
1025
|
+
sliceId: nextSliceId,
|
|
1026
|
+
state: "awaiting_resume",
|
|
1027
|
+
piMessages,
|
|
1028
|
+
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
1029
|
+
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
1030
|
+
resumeReason: "timeout",
|
|
1031
|
+
resumedFromSliceId: args.currentSliceId,
|
|
1032
|
+
errorMessage: args.errorMessage,
|
|
1033
|
+
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
1034
|
+
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {}
|
|
822
1035
|
});
|
|
1036
|
+
} catch (recordError) {
|
|
1037
|
+
logSessionRecordError(
|
|
1038
|
+
recordError,
|
|
1039
|
+
"agent_continue_session_record_failed",
|
|
1040
|
+
args,
|
|
1041
|
+
{
|
|
1042
|
+
"app.ai.resume_from_slice_id": args.currentSliceId,
|
|
1043
|
+
"app.ai.resume_next_slice_id": nextSliceId
|
|
1044
|
+
},
|
|
1045
|
+
"Failed to persist session record before scheduling agent continuation"
|
|
1046
|
+
);
|
|
1047
|
+
return void 0;
|
|
823
1048
|
}
|
|
824
|
-
return new ProviderCredentialRouter({ brokersByProvider });
|
|
825
1049
|
}
|
|
826
|
-
function
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
new StateAdapterTokenStore(stateAdapter)
|
|
1050
|
+
async function persistYieldSessionRecord(args) {
|
|
1051
|
+
try {
|
|
1052
|
+
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
1053
|
+
args.conversationId,
|
|
1054
|
+
args.sessionId
|
|
832
1055
|
);
|
|
833
|
-
|
|
1056
|
+
const piMessages = resumableBoundary(
|
|
1057
|
+
args.messages,
|
|
1058
|
+
latestSessionRecord?.piMessages
|
|
1059
|
+
);
|
|
1060
|
+
if (piMessages.length === 0 || !isContinuableBoundary(piMessages)) {
|
|
1061
|
+
return void 0;
|
|
1062
|
+
}
|
|
1063
|
+
return await upsertAgentTurnSessionRecord({
|
|
1064
|
+
...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
|
|
1065
|
+
conversationId: args.conversationId,
|
|
1066
|
+
cumulativeDurationMs: addDurationMs(
|
|
1067
|
+
latestSessionRecord?.cumulativeDurationMs,
|
|
1068
|
+
args.currentDurationMs
|
|
1069
|
+
),
|
|
1070
|
+
cumulativeUsage: addAgentTurnUsage(
|
|
1071
|
+
latestSessionRecord?.cumulativeUsage,
|
|
1072
|
+
args.currentUsage
|
|
1073
|
+
),
|
|
1074
|
+
...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
|
|
1075
|
+
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
1076
|
+
sessionId: args.sessionId,
|
|
1077
|
+
sliceId: args.currentSliceId,
|
|
1078
|
+
state: "awaiting_resume",
|
|
1079
|
+
piMessages,
|
|
1080
|
+
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
1081
|
+
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
1082
|
+
resumeReason: "yield",
|
|
1083
|
+
resumedFromSliceId: latestSessionRecord?.resumedFromSliceId,
|
|
1084
|
+
errorMessage: args.errorMessage,
|
|
1085
|
+
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
1086
|
+
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {}
|
|
1087
|
+
});
|
|
1088
|
+
} catch (recordError) {
|
|
1089
|
+
logSessionRecordError(
|
|
1090
|
+
recordError,
|
|
1091
|
+
"agent_turn_yield_session_record_failed",
|
|
1092
|
+
args,
|
|
1093
|
+
{
|
|
1094
|
+
"app.ai.resume_slice_id": args.currentSliceId
|
|
1095
|
+
},
|
|
1096
|
+
"Failed to persist cooperative yield session record"
|
|
1097
|
+
);
|
|
1098
|
+
return void 0;
|
|
834
1099
|
}
|
|
835
|
-
return router;
|
|
836
|
-
}
|
|
837
|
-
async function issueProviderCredentialLease(input) {
|
|
838
|
-
return await getSandboxEgressRouter().issue(input);
|
|
839
1100
|
}
|
|
840
1101
|
|
|
841
|
-
// src/chat/
|
|
842
|
-
import {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1102
|
+
// src/chat/respond.ts
|
|
1103
|
+
import { Agent as Agent2 } from "@earendil-works/pi-agent-core";
|
|
1104
|
+
import { THREAD_STATE_TTL_MS as THREAD_STATE_TTL_MS4 } from "chat";
|
|
1105
|
+
import { z as z4 } from "zod";
|
|
1106
|
+
|
|
1107
|
+
// src/chat/capabilities/catalog.ts
|
|
1108
|
+
var cachedCatalog;
|
|
1109
|
+
function getCapabilityCatalog() {
|
|
1110
|
+
const signature = pluginCatalogRuntime.getSignature();
|
|
1111
|
+
if (cachedCatalog?.signature === signature) return cachedCatalog;
|
|
1112
|
+
const providers = pluginCatalogRuntime.getCapabilityProviders();
|
|
1113
|
+
const capabilityToProvider = /* @__PURE__ */ new Map();
|
|
1114
|
+
for (const provider of providers) {
|
|
1115
|
+
for (const capability of provider.capabilities) {
|
|
1116
|
+
if (capabilityToProvider.has(capability)) {
|
|
1117
|
+
throw new Error(
|
|
1118
|
+
`Duplicate capability registration for "${capability}"`
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
capabilityToProvider.set(capability, provider);
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
cachedCatalog = { signature, providers, capabilityToProvider };
|
|
1125
|
+
return cachedCatalog;
|
|
1126
|
+
}
|
|
1127
|
+
var catalogLogged = false;
|
|
1128
|
+
function logCapabilityCatalogLoadedOnce() {
|
|
1129
|
+
if (catalogLogged) return;
|
|
1130
|
+
catalogLogged = true;
|
|
1131
|
+
const { providers } = getCapabilityCatalog();
|
|
1132
|
+
const capabilityNames = providers.flatMap((p) => p.capabilities).sort();
|
|
1133
|
+
const configKeys = [
|
|
1134
|
+
...new Set(providers.flatMap((p) => p.configKeys))
|
|
1135
|
+
].sort();
|
|
1136
|
+
logInfo(
|
|
1137
|
+
"capability_catalog_loaded",
|
|
1138
|
+
{},
|
|
1139
|
+
{
|
|
1140
|
+
"app.capability.providers": providers.map((p) => p.provider),
|
|
1141
|
+
"app.capability.count": capabilityNames.length,
|
|
1142
|
+
"app.capability.names": capabilityNames,
|
|
1143
|
+
"app.config.key_count": configKeys.length,
|
|
1144
|
+
"app.config.keys": configKeys
|
|
1145
|
+
},
|
|
1146
|
+
"Loaded capability provider catalog"
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// src/chat/capabilities/router.ts
|
|
1151
|
+
var ProviderCredentialRouter = class {
|
|
1152
|
+
brokersByProvider;
|
|
1153
|
+
constructor(input) {
|
|
1154
|
+
this.brokersByProvider = input.brokersByProvider;
|
|
1155
|
+
}
|
|
1156
|
+
async issue(input) {
|
|
1157
|
+
const broker = this.brokersByProvider[input.provider];
|
|
1158
|
+
if (!broker) {
|
|
1159
|
+
throw new Error(
|
|
1160
|
+
`No credential broker registered for provider: ${input.provider}`
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
return await broker.issue({
|
|
1164
|
+
context: input.context,
|
|
1165
|
+
reason: input.reason
|
|
1166
|
+
});
|
|
1167
|
+
}
|
|
1168
|
+
};
|
|
1169
|
+
|
|
1170
|
+
// src/chat/credentials/user-token-store.ts
|
|
1171
|
+
import {
|
|
1172
|
+
pluginStoredTokensSchema
|
|
1173
|
+
} from "@sentry/junior-plugin-api";
|
|
1174
|
+
var storedTokensSchema = pluginStoredTokensSchema;
|
|
1175
|
+
|
|
1176
|
+
// src/chat/credentials/state-adapter-token-store.ts
|
|
1177
|
+
var KEY_PREFIX = "oauth-token";
|
|
1178
|
+
var BUFFER_MS = 24 * 60 * 60 * 1e3;
|
|
1179
|
+
var LONG_LIVED_TTL_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
1180
|
+
var REFRESH_LOCK_WAIT_MS = 3e4;
|
|
1181
|
+
var REFRESH_LOCK_RETRY_MS = 100;
|
|
1182
|
+
function tokenKey(userId, provider) {
|
|
1183
|
+
return `${KEY_PREFIX}:${userId}:${provider}`;
|
|
1184
|
+
}
|
|
1185
|
+
function refreshLockKey(userId, provider) {
|
|
1186
|
+
return `${tokenKey(userId, provider)}:refresh`;
|
|
1187
|
+
}
|
|
1188
|
+
async function sleep(ms) {
|
|
1189
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
1190
|
+
}
|
|
1191
|
+
var StateAdapterTokenStore = class {
|
|
1192
|
+
state;
|
|
1193
|
+
constructor(stateAdapter) {
|
|
1194
|
+
this.state = stateAdapter;
|
|
1195
|
+
}
|
|
1196
|
+
async get(userId, provider) {
|
|
1197
|
+
const stored = await this.state.get(tokenKey(userId, provider));
|
|
1198
|
+
return stored === null || stored === void 0 ? void 0 : storedTokensSchema.parse(stored);
|
|
1199
|
+
}
|
|
1200
|
+
async set(userId, provider, tokens) {
|
|
1201
|
+
const parsed = storedTokensSchema.parse(tokens);
|
|
1202
|
+
const expiresAt = parsed.refreshTokenExpiresAt ?? parsed.expiresAt;
|
|
1203
|
+
const ttlMs2 = expiresAt ? Math.max(expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
|
|
1204
|
+
await this.state.set(tokenKey(userId, provider), parsed, ttlMs2);
|
|
1205
|
+
}
|
|
1206
|
+
async delete(userId, provider) {
|
|
1207
|
+
await this.state.delete(tokenKey(userId, provider));
|
|
1208
|
+
}
|
|
1209
|
+
/** Wait for the per-slot refresh gate so rotated refresh tokens are used once. */
|
|
1210
|
+
async withRefresh(userId, provider, callback) {
|
|
1211
|
+
const lockKey = refreshLockKey(userId, provider);
|
|
1212
|
+
const deadline = Date.now() + REFRESH_LOCK_WAIT_MS;
|
|
1213
|
+
while (true) {
|
|
1214
|
+
const lock = await acquireActiveLock(this.state, lockKey);
|
|
1215
|
+
if (lock) {
|
|
1216
|
+
try {
|
|
1217
|
+
return await callback();
|
|
1218
|
+
} finally {
|
|
1219
|
+
await this.state.releaseLock(lock);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
if (Date.now() >= deadline) {
|
|
1223
|
+
throw new Error(`Could not acquire OAuth token refresh lock`);
|
|
1224
|
+
}
|
|
1225
|
+
await sleep(REFRESH_LOCK_RETRY_MS);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
|
|
1230
|
+
// src/chat/capabilities/factory.ts
|
|
1231
|
+
var sandboxEgressRouters = /* @__PURE__ */ new WeakMap();
|
|
1232
|
+
function createUserTokenStore() {
|
|
1233
|
+
return new StateAdapterTokenStore(getStateAdapter());
|
|
1234
|
+
}
|
|
1235
|
+
function createProviderCredentialRouter(userTokenStore) {
|
|
1236
|
+
logCapabilityCatalogLoadedOnce();
|
|
1237
|
+
const brokersByProvider = {};
|
|
1238
|
+
for (const plugin of pluginCatalogRuntime.getProviders()) {
|
|
1239
|
+
const { name } = plugin.manifest;
|
|
1240
|
+
if (!plugin.manifest.credentials && !plugin.manifest.apiHeaders) {
|
|
1241
|
+
continue;
|
|
1242
|
+
}
|
|
1243
|
+
brokersByProvider[name] = pluginCatalogRuntime.createBroker(name, {
|
|
1244
|
+
userTokenStore
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
return new ProviderCredentialRouter({ brokersByProvider });
|
|
1248
|
+
}
|
|
1249
|
+
function getSandboxEgressRouter() {
|
|
1250
|
+
const stateAdapter = getStateAdapter();
|
|
1251
|
+
let router = sandboxEgressRouters.get(stateAdapter);
|
|
1252
|
+
if (!router) {
|
|
1253
|
+
router = createProviderCredentialRouter(
|
|
1254
|
+
new StateAdapterTokenStore(stateAdapter)
|
|
1255
|
+
);
|
|
1256
|
+
sandboxEgressRouters.set(stateAdapter, router);
|
|
1257
|
+
}
|
|
1258
|
+
return router;
|
|
1259
|
+
}
|
|
1260
|
+
async function issueProviderCredentialLease(input) {
|
|
1261
|
+
return await getSandboxEgressRouter().issue(input);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// src/chat/capabilities/jr-rpc-command.ts
|
|
1265
|
+
import { Bash, defineCommand } from "just-bash";
|
|
1266
|
+
function commandResult(input) {
|
|
1267
|
+
let stdout = "";
|
|
1268
|
+
if (typeof input.stdout === "string") {
|
|
1269
|
+
stdout = input.stdout;
|
|
1270
|
+
} else if (input.stdout !== void 0) {
|
|
1271
|
+
stdout = `${JSON.stringify(input.stdout, null, 2)}
|
|
1272
|
+
`;
|
|
1273
|
+
}
|
|
1274
|
+
return {
|
|
1275
|
+
stdout,
|
|
1276
|
+
stderr: input.stderr ?? "",
|
|
1277
|
+
exitCode: input.exitCode
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
function requireChannelConfiguration(deps) {
|
|
858
1281
|
if (deps.channelConfiguration) {
|
|
859
1282
|
return { ok: true, configuration: deps.channelConfiguration };
|
|
860
1283
|
}
|
|
@@ -4467,6 +4890,7 @@ async function getPermalinkBestEffort(args) {
|
|
|
4467
4890
|
3,
|
|
4468
4891
|
{
|
|
4469
4892
|
action: "chat.getPermalink",
|
|
4893
|
+
idempotent: true,
|
|
4470
4894
|
spanAttributes: {
|
|
4471
4895
|
"app.slack.channel_id": args.channelId,
|
|
4472
4896
|
"app.slack.message_ts": args.messageTs
|
|
@@ -4536,6 +4960,7 @@ async function deleteSlackMessage(input) {
|
|
|
4536
4960
|
3,
|
|
4537
4961
|
{
|
|
4538
4962
|
action: "chat.delete",
|
|
4963
|
+
idempotent: true,
|
|
4539
4964
|
spanAttributes: {
|
|
4540
4965
|
"app.slack.channel_id": channelId,
|
|
4541
4966
|
"app.slack.message_ts": timestamp
|
|
@@ -4644,6 +5069,7 @@ async function addReactionToMessage(input) {
|
|
|
4644
5069
|
3,
|
|
4645
5070
|
{
|
|
4646
5071
|
action: "reactions.add",
|
|
5072
|
+
idempotent: true,
|
|
4647
5073
|
spanAttributes: {
|
|
4648
5074
|
"app.slack.channel_id": channelId,
|
|
4649
5075
|
"app.slack.message_ts": timestamp,
|
|
@@ -4682,6 +5108,7 @@ async function removeReactionFromMessage(input) {
|
|
|
4682
5108
|
3,
|
|
4683
5109
|
{
|
|
4684
5110
|
action: "reactions.remove",
|
|
5111
|
+
idempotent: true,
|
|
4685
5112
|
spanAttributes: {
|
|
4686
5113
|
"app.slack.channel_id": channelId,
|
|
4687
5114
|
"app.slack.message_ts": timestamp,
|
|
@@ -5747,6 +6174,31 @@ function createSlackListUpdateItemTool(state) {
|
|
|
5747
6174
|
// src/chat/tools/slack/thread-read.ts
|
|
5748
6175
|
import { Type as Type19 } from "@sinclair/typebox";
|
|
5749
6176
|
|
|
6177
|
+
// src/chat/tools/slack/channel-access.ts
|
|
6178
|
+
async function checkSlackChannelReadAccess(args) {
|
|
6179
|
+
const target = normalizeSlackConversationId(args.targetChannelId);
|
|
6180
|
+
if (!target) {
|
|
6181
|
+
return { allowed: false, error: "Invalid Slack channel ID." };
|
|
6182
|
+
}
|
|
6183
|
+
const currentChannels = args.currentChannelIds.map((channelId) => normalizeSlackConversationId(channelId)).filter((channelId) => Boolean(channelId));
|
|
6184
|
+
if (currentChannels.includes(target)) {
|
|
6185
|
+
return { allowed: true };
|
|
6186
|
+
}
|
|
6187
|
+
const store = args.store ?? getConversationStore();
|
|
6188
|
+
const visibility = await store.getDestinationVisibility({
|
|
6189
|
+
provider: "slack",
|
|
6190
|
+
providerTenantId: args.teamId,
|
|
6191
|
+
providerDestinationId: target
|
|
6192
|
+
});
|
|
6193
|
+
if (visibility === "public") {
|
|
6194
|
+
return { allowed: true };
|
|
6195
|
+
}
|
|
6196
|
+
return {
|
|
6197
|
+
allowed: false,
|
|
6198
|
+
error: "Cannot read this Slack conversation: only the current conversation or public channels Junior has seen in this workspace are readable."
|
|
6199
|
+
};
|
|
6200
|
+
}
|
|
6201
|
+
|
|
5750
6202
|
// src/chat/tools/slack/slack-message-url.ts
|
|
5751
6203
|
var SLACK_HOST_PATTERN = /^[a-z0-9-]+\.slack(?:-gov)?\.com$/;
|
|
5752
6204
|
var ARCHIVE_PATH_PATTERN = /^\/archives\/([CDG][A-Z0-9]+)\/p(\d{10})(\d{6})$/;
|
|
@@ -5906,26 +6358,9 @@ function truncateMessages(messages, maxChars) {
|
|
|
5906
6358
|
}
|
|
5907
6359
|
return { messages: kept, omitted: messages.length - kept.length };
|
|
5908
6360
|
}
|
|
5909
|
-
function
|
|
5910
|
-
const target = normalizeSlackConversationId(targetChannelId);
|
|
5911
|
-
const current = normalizeSlackConversationId(currentChannelId);
|
|
5912
|
-
if (!target) {
|
|
5913
|
-
return { allowed: false, error: "Invalid Slack channel ID." };
|
|
5914
|
-
}
|
|
5915
|
-
if (target.startsWith("C")) {
|
|
5916
|
-
return { allowed: true };
|
|
5917
|
-
}
|
|
5918
|
-
if (target === current) {
|
|
5919
|
-
return { allowed: true };
|
|
5920
|
-
}
|
|
5921
|
-
return {
|
|
5922
|
-
allowed: false,
|
|
5923
|
-
error: "Cannot read private channels or DMs unless the link is from the current conversation."
|
|
5924
|
-
};
|
|
5925
|
-
}
|
|
5926
|
-
function createSlackThreadReadTool(context) {
|
|
6361
|
+
function createSlackThreadReadTool(context, deps = {}) {
|
|
5927
6362
|
return tool({
|
|
5928
|
-
description: "Read a Slack thread from a shared Slack message archive URL or explicit channel + timestamp. Use when the user shares a Slack message link (https://*.slack.com/archives/...) and you need the referenced message and its thread context.
|
|
6363
|
+
description: "Read a Slack thread from a shared Slack message archive URL or explicit channel + timestamp. Use when the user shares a Slack message link (https://*.slack.com/archives/...) and you need the referenced message and its thread context. Only the current conversation and public channels Junior has seen in this workspace are readable.",
|
|
5929
6364
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
5930
6365
|
inputSchema: Type19.Object({
|
|
5931
6366
|
url: Type19.Optional(
|
|
@@ -5985,10 +6420,15 @@ function createSlackThreadReadTool(context) {
|
|
|
5985
6420
|
error: "Provide either a Slack message `url` or both `channel_id` and `ts`."
|
|
5986
6421
|
};
|
|
5987
6422
|
}
|
|
5988
|
-
const access =
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
6423
|
+
const access = await checkSlackChannelReadAccess({
|
|
6424
|
+
currentChannelIds: [
|
|
6425
|
+
context.destinationChannelId,
|
|
6426
|
+
context.sourceChannelId
|
|
6427
|
+
],
|
|
6428
|
+
store: deps.visibilityStore,
|
|
6429
|
+
targetChannelId: channelId,
|
|
6430
|
+
teamId: context.teamId
|
|
6431
|
+
});
|
|
5992
6432
|
if (!access.allowed) {
|
|
5993
6433
|
return {
|
|
5994
6434
|
ok: false,
|
|
@@ -10862,11 +11302,7 @@ function buildClassifierPrompt(args) {
|
|
|
10862
11302
|
);
|
|
10863
11303
|
}
|
|
10864
11304
|
}
|
|
10865
|
-
sections.push(
|
|
10866
|
-
"<current-instruction>",
|
|
10867
|
-
args.messageText.trim() || "[empty]",
|
|
10868
|
-
"</current-instruction>"
|
|
10869
|
-
);
|
|
11305
|
+
sections.push(renderCurrentInstruction(args.messageText.trim() || "[empty]"));
|
|
10870
11306
|
for (const block of args.currentTurnBlocks ?? []) {
|
|
10871
11307
|
const trimmed = block.trim();
|
|
10872
11308
|
if (!trimmed) {
|
|
@@ -10875,499 +11311,119 @@ function buildClassifierPrompt(args) {
|
|
|
10875
11311
|
sections.push("", trimmed);
|
|
10876
11312
|
}
|
|
10877
11313
|
return sections.join("\n");
|
|
10878
|
-
}
|
|
10879
|
-
async function selectTurnThinkingLevel(args) {
|
|
10880
|
-
const trimmedContext = trimContextForRouter(args.conversationContext);
|
|
10881
|
-
const instructionLength = args.messageText.trim().length;
|
|
10882
|
-
const turnBlockCount = (args.currentTurnBlocks ?? []).filter(
|
|
10883
|
-
(block) => block.trim().length > 0
|
|
10884
|
-
).length;
|
|
10885
|
-
const prompt = buildClassifierPrompt({
|
|
10886
|
-
conversationContext: trimmedContext,
|
|
10887
|
-
currentTurnBlocks: args.currentTurnBlocks,
|
|
10888
|
-
messageText: args.messageText
|
|
10889
|
-
});
|
|
10890
|
-
const logContext = {
|
|
10891
|
-
slackThreadId: args.context?.threadId,
|
|
10892
|
-
slackChannelId: args.context?.channelId,
|
|
10893
|
-
slackUserId: args.context?.requesterId,
|
|
10894
|
-
runId: args.context?.runId,
|
|
10895
|
-
modelId: args.fastModelId
|
|
10896
|
-
};
|
|
10897
|
-
return withSpan(
|
|
10898
|
-
"chat.route_thinking",
|
|
10899
|
-
"chat.route_thinking",
|
|
10900
|
-
logContext,
|
|
10901
|
-
async () => {
|
|
10902
|
-
setSpanAttributes({
|
|
10903
|
-
"app.ai.router.prompt_char_count": prompt.length,
|
|
10904
|
-
"app.ai.router.instruction_char_count": instructionLength,
|
|
10905
|
-
"app.ai.router.context_char_count": trimmedContext?.originalCharCount ?? 0,
|
|
10906
|
-
"app.ai.router.context_trimmed": trimmedContext?.truncated ?? false,
|
|
10907
|
-
"app.ai.router.turn_block_count": turnBlockCount
|
|
10908
|
-
});
|
|
10909
|
-
const selection = await classifyTurn({
|
|
10910
|
-
completeObject: args.completeObject,
|
|
10911
|
-
fastModelId: args.fastModelId,
|
|
10912
|
-
metadata: {
|
|
10913
|
-
modelId: args.fastModelId,
|
|
10914
|
-
threadId: args.context?.threadId ?? "",
|
|
10915
|
-
channelId: args.context?.channelId ?? "",
|
|
10916
|
-
requesterId: args.context?.requesterId ?? "",
|
|
10917
|
-
runId: args.context?.runId ?? ""
|
|
10918
|
-
},
|
|
10919
|
-
prompt
|
|
10920
|
-
});
|
|
10921
|
-
const normalizedSelection = applyThinkingFloor(selection, {
|
|
10922
|
-
minimum: trimmedContext || turnBlockCount > 0 ? "medium" : void 0
|
|
10923
|
-
});
|
|
10924
|
-
setSpanAttributes({
|
|
10925
|
-
"app.ai.thinking_level": normalizedSelection.thinkingLevel,
|
|
10926
|
-
"app.ai.thinking_level_reason": normalizedSelection.reason,
|
|
10927
|
-
...normalizedSelection.confidence !== void 0 ? {
|
|
10928
|
-
"app.ai.thinking_level_confidence": normalizedSelection.confidence
|
|
10929
|
-
} : {}
|
|
10930
|
-
});
|
|
10931
|
-
return normalizedSelection;
|
|
10932
|
-
}
|
|
10933
|
-
);
|
|
10934
|
-
}
|
|
10935
|
-
function applyThinkingFloor(selection, args) {
|
|
10936
|
-
const minimum = args.minimum;
|
|
10937
|
-
if (!minimum || selection.thinkingLevel === "none" || THINKING_LEVEL_RANK[selection.thinkingLevel] >= THINKING_LEVEL_RANK[minimum]) {
|
|
10938
|
-
return selection;
|
|
10939
|
-
}
|
|
10940
|
-
return {
|
|
10941
|
-
...selection,
|
|
10942
|
-
thinkingLevel: minimum,
|
|
10943
|
-
reason: `thinking_floor:${minimum}:${selection.reason}`
|
|
10944
|
-
};
|
|
10945
|
-
}
|
|
10946
|
-
async function classifyTurn(args) {
|
|
10947
|
-
try {
|
|
10948
|
-
const result = await args.completeObject({
|
|
10949
|
-
modelId: args.fastModelId,
|
|
10950
|
-
schema: turnExecutionProfileSchema,
|
|
10951
|
-
maxTokens: 120,
|
|
10952
|
-
metadata: args.metadata,
|
|
10953
|
-
prompt: args.prompt,
|
|
10954
|
-
thinkingLevel: "low",
|
|
10955
|
-
system: buildClassifierSystemPrompt(),
|
|
10956
|
-
temperature: 0
|
|
10957
|
-
});
|
|
10958
|
-
const parsed = turnExecutionProfileSchema.parse(result.object);
|
|
10959
|
-
const reason = parsed.reason.trim();
|
|
10960
|
-
if (parsed.confidence < CLASSIFIER_CONFIDENCE_THRESHOLD) {
|
|
10961
|
-
return {
|
|
10962
|
-
confidence: parsed.confidence,
|
|
10963
|
-
thinkingLevel: DEFAULT_THINKING_LEVEL,
|
|
10964
|
-
reason: `low_confidence_medium_default:${reason}`
|
|
10965
|
-
};
|
|
10966
|
-
}
|
|
10967
|
-
return {
|
|
10968
|
-
confidence: parsed.confidence,
|
|
10969
|
-
thinkingLevel: parsed.thinking_level,
|
|
10970
|
-
reason
|
|
10971
|
-
};
|
|
10972
|
-
} catch {
|
|
10973
|
-
return {
|
|
10974
|
-
thinkingLevel: DEFAULT_THINKING_LEVEL,
|
|
10975
|
-
reason: "classifier_error_default"
|
|
10976
|
-
};
|
|
10977
|
-
}
|
|
10978
|
-
}
|
|
10979
|
-
function toAgentThinkingLevel(level) {
|
|
10980
|
-
switch (level) {
|
|
10981
|
-
case "none":
|
|
10982
|
-
return "off";
|
|
10983
|
-
case "low":
|
|
10984
|
-
return "low";
|
|
10985
|
-
case "medium":
|
|
10986
|
-
return "medium";
|
|
10987
|
-
case "high":
|
|
10988
|
-
return "high";
|
|
10989
|
-
case "xhigh":
|
|
10990
|
-
return "xhigh";
|
|
10991
|
-
}
|
|
10992
|
-
}
|
|
10993
|
-
|
|
10994
|
-
// src/chat/usage.ts
|
|
10995
|
-
var COMPONENT_USAGE_FIELDS = [
|
|
10996
|
-
"inputTokens",
|
|
10997
|
-
"outputTokens",
|
|
10998
|
-
"cachedInputTokens",
|
|
10999
|
-
"cacheCreationTokens"
|
|
11000
|
-
];
|
|
11001
|
-
function hasAgentTurnUsage(usage) {
|
|
11002
|
-
return Boolean(
|
|
11003
|
-
usage && Object.values(usage).some(
|
|
11004
|
-
(value) => typeof value === "number" && Number.isFinite(value)
|
|
11005
|
-
)
|
|
11006
|
-
);
|
|
11007
|
-
}
|
|
11008
|
-
function getFiniteCount(value) {
|
|
11009
|
-
return typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : void 0;
|
|
11010
|
-
}
|
|
11011
|
-
function getComponentTotal(usage) {
|
|
11012
|
-
let total;
|
|
11013
|
-
for (const field of COMPONENT_USAGE_FIELDS) {
|
|
11014
|
-
const value = getFiniteCount(usage[field]);
|
|
11015
|
-
if (value === void 0) continue;
|
|
11016
|
-
total = (total ?? 0) + value;
|
|
11017
|
-
}
|
|
11018
|
-
return total;
|
|
11019
|
-
}
|
|
11020
|
-
function addAgentTurnUsage(...usages) {
|
|
11021
|
-
const components = {};
|
|
11022
|
-
let componentTotal;
|
|
11023
|
-
let totalOnlyTokens;
|
|
11024
|
-
for (const usage of usages) {
|
|
11025
|
-
if (!usage) continue;
|
|
11026
|
-
const usageComponentTotal = getComponentTotal(usage);
|
|
11027
|
-
if (usageComponentTotal !== void 0) {
|
|
11028
|
-
componentTotal = (componentTotal ?? 0) + usageComponentTotal;
|
|
11029
|
-
for (const field of COMPONENT_USAGE_FIELDS) {
|
|
11030
|
-
const value = getFiniteCount(usage[field]);
|
|
11031
|
-
if (value === void 0) continue;
|
|
11032
|
-
components[field] = (components[field] ?? 0) + value;
|
|
11033
|
-
}
|
|
11034
|
-
continue;
|
|
11035
|
-
}
|
|
11036
|
-
const totalTokens = getFiniteCount(usage.totalTokens);
|
|
11037
|
-
if (totalTokens !== void 0) {
|
|
11038
|
-
totalOnlyTokens = (totalOnlyTokens ?? 0) + totalTokens;
|
|
11039
|
-
}
|
|
11040
|
-
}
|
|
11041
|
-
if (totalOnlyTokens !== void 0) {
|
|
11042
|
-
return {
|
|
11043
|
-
totalTokens: totalOnlyTokens + (componentTotal ?? 0)
|
|
11044
|
-
};
|
|
11045
|
-
}
|
|
11046
|
-
return hasAgentTurnUsage(components) ? components : void 0;
|
|
11047
|
-
}
|
|
11048
|
-
|
|
11049
|
-
// src/chat/services/turn-session-record.ts
|
|
11050
|
-
var AGENT_CONTINUE_MAX_SLICES = 48;
|
|
11051
|
-
function logSessionRecordError(error, eventName, args, attributes, message) {
|
|
11052
|
-
logException(
|
|
11053
|
-
error,
|
|
11054
|
-
eventName,
|
|
11055
|
-
{
|
|
11056
|
-
slackThreadId: args.logContext.threadId,
|
|
11057
|
-
slackUserId: args.logContext.requesterId,
|
|
11058
|
-
slackChannelId: args.logContext.channelId,
|
|
11059
|
-
runId: args.logContext.runId,
|
|
11060
|
-
assistantUserName: args.logContext.assistantUserName,
|
|
11061
|
-
modelId: args.logContext.modelId
|
|
11062
|
-
},
|
|
11063
|
-
{
|
|
11064
|
-
"app.ai.resume_conversation_id": args.conversationId,
|
|
11065
|
-
"app.ai.resume_session_id": args.sessionId,
|
|
11066
|
-
...attributes
|
|
11067
|
-
},
|
|
11068
|
-
message
|
|
11069
|
-
);
|
|
11070
|
-
}
|
|
11071
|
-
function addDurationMs(prior, current) {
|
|
11072
|
-
const total = [prior, current].reduce((sum, value) => {
|
|
11073
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
11074
|
-
return sum;
|
|
11075
|
-
}
|
|
11076
|
-
return (sum ?? 0) + Math.max(0, Math.floor(value));
|
|
11077
|
-
}, void 0);
|
|
11078
|
-
return total;
|
|
11079
|
-
}
|
|
11080
|
-
function isContinuableBoundary(messages) {
|
|
11081
|
-
const lastRole = getPiMessageRole(messages.at(-1));
|
|
11082
|
-
return lastRole === "user" || lastRole === "toolResult";
|
|
11083
|
-
}
|
|
11084
|
-
function resumableBoundary(messages, fallbackMessages) {
|
|
11085
|
-
const current = trimTrailingAssistantMessages(messages);
|
|
11086
|
-
if (current.length > 0 && isContinuableBoundary(current)) {
|
|
11087
|
-
return current;
|
|
11088
|
-
}
|
|
11089
|
-
return trimTrailingAssistantMessages(fallbackMessages ?? []);
|
|
11090
|
-
}
|
|
11091
|
-
async function loadTurnSessionRecord(ctx) {
|
|
11092
|
-
const canUseTurnSession = Boolean(ctx.conversationId && ctx.sessionId);
|
|
11093
|
-
const existingSessionRecord = canUseTurnSession && ctx.conversationId && ctx.sessionId ? await getAgentTurnSessionRecord(ctx.conversationId, ctx.sessionId) : void 0;
|
|
11094
|
-
const hasAwaitingResumeRecord = Boolean(
|
|
11095
|
-
existingSessionRecord && existingSessionRecord.state === "awaiting_resume"
|
|
11096
|
-
);
|
|
11097
|
-
return {
|
|
11098
|
-
canUseTurnSession,
|
|
11099
|
-
resumedFromSessionRecord: hasAwaitingResumeRecord,
|
|
11100
|
-
currentSliceId: hasAwaitingResumeRecord ? existingSessionRecord.sliceId : 1,
|
|
11101
|
-
existingSessionRecord
|
|
11102
|
-
};
|
|
11103
|
-
}
|
|
11104
|
-
async function persistRunningSessionRecord(args) {
|
|
11105
|
-
if (args.messages.length === 0 || !isContinuableBoundary(args.messages)) {
|
|
11106
|
-
return false;
|
|
11107
|
-
}
|
|
11108
|
-
try {
|
|
11109
|
-
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
11110
|
-
args.conversationId,
|
|
11111
|
-
args.sessionId
|
|
11112
|
-
);
|
|
11113
|
-
await upsertAgentTurnSessionRecord({
|
|
11114
|
-
...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
|
|
11115
|
-
conversationId: args.conversationId,
|
|
11116
|
-
cumulativeDurationMs: latestSessionRecord?.cumulativeDurationMs,
|
|
11117
|
-
cumulativeUsage: latestSessionRecord?.cumulativeUsage,
|
|
11118
|
-
...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
|
|
11119
|
-
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
11120
|
-
sessionId: args.sessionId,
|
|
11121
|
-
sliceId: args.sliceId,
|
|
11122
|
-
state: "running",
|
|
11123
|
-
piMessages: args.messages,
|
|
11124
|
-
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
11125
|
-
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
11126
|
-
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
11127
|
-
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {},
|
|
11128
|
-
...(args.turnStartMessageIndex ?? latestSessionRecord?.turnStartMessageIndex) !== void 0 ? {
|
|
11129
|
-
turnStartMessageIndex: args.turnStartMessageIndex ?? latestSessionRecord?.turnStartMessageIndex
|
|
11130
|
-
} : {}
|
|
11131
|
-
});
|
|
11132
|
-
return true;
|
|
11133
|
-
} catch (recordError) {
|
|
11134
|
-
logSessionRecordError(
|
|
11135
|
-
recordError,
|
|
11136
|
-
"agent_turn_running_session_record_failed",
|
|
11137
|
-
args,
|
|
11138
|
-
{
|
|
11139
|
-
"app.ai.resume_slice_id": args.sliceId
|
|
11140
|
-
},
|
|
11141
|
-
"Failed to persist running turn session record"
|
|
11142
|
-
);
|
|
11143
|
-
return false;
|
|
11144
|
-
}
|
|
11145
|
-
}
|
|
11146
|
-
async function persistCompletedSessionRecord(args) {
|
|
11147
|
-
try {
|
|
11148
|
-
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
11149
|
-
args.conversationId,
|
|
11150
|
-
args.sessionId
|
|
11151
|
-
);
|
|
11152
|
-
await upsertAgentTurnSessionRecord({
|
|
11153
|
-
...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
|
|
11154
|
-
conversationId: args.conversationId,
|
|
11155
|
-
cumulativeDurationMs: addDurationMs(
|
|
11156
|
-
latestSessionRecord?.cumulativeDurationMs,
|
|
11157
|
-
args.currentDurationMs
|
|
11158
|
-
),
|
|
11159
|
-
cumulativeUsage: addAgentTurnUsage(
|
|
11160
|
-
latestSessionRecord?.cumulativeUsage,
|
|
11161
|
-
args.currentUsage
|
|
11162
|
-
),
|
|
11163
|
-
...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
|
|
11164
|
-
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
11165
|
-
sessionId: args.sessionId,
|
|
11166
|
-
sliceId: args.sliceId,
|
|
11167
|
-
state: "completed",
|
|
11168
|
-
piMessages: args.allMessages,
|
|
11169
|
-
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
11170
|
-
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
11171
|
-
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
11172
|
-
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {},
|
|
11173
|
-
...(args.turnStartMessageIndex ?? latestSessionRecord?.turnStartMessageIndex) !== void 0 ? {
|
|
11174
|
-
turnStartMessageIndex: args.turnStartMessageIndex ?? latestSessionRecord?.turnStartMessageIndex
|
|
11175
|
-
} : {}
|
|
11176
|
-
});
|
|
11177
|
-
} catch (recordError) {
|
|
11178
|
-
logSessionRecordError(
|
|
11179
|
-
recordError,
|
|
11180
|
-
"agent_turn_completed_session_record_failed",
|
|
11181
|
-
args,
|
|
11182
|
-
{
|
|
11183
|
-
"app.ai.resume_slice_id": args.sliceId
|
|
11184
|
-
},
|
|
11185
|
-
"Failed to persist completed turn session record"
|
|
11186
|
-
);
|
|
11187
|
-
}
|
|
11188
|
-
}
|
|
11189
|
-
async function persistAuthPauseSessionRecord(args) {
|
|
11190
|
-
const nextSliceId = args.currentSliceId + 1;
|
|
11191
|
-
try {
|
|
11192
|
-
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
11193
|
-
args.conversationId,
|
|
11194
|
-
args.sessionId
|
|
11195
|
-
);
|
|
11196
|
-
const piMessages = resumableBoundary(
|
|
11197
|
-
args.messages,
|
|
11198
|
-
latestSessionRecord?.piMessages
|
|
11199
|
-
);
|
|
11200
|
-
if (piMessages.length > 0 && !isContinuableBoundary(piMessages)) {
|
|
11201
|
-
return void 0;
|
|
11202
|
-
}
|
|
11203
|
-
return await upsertAgentTurnSessionRecord({
|
|
11204
|
-
...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
|
|
11205
|
-
conversationId: args.conversationId,
|
|
11206
|
-
cumulativeDurationMs: addDurationMs(
|
|
11207
|
-
latestSessionRecord?.cumulativeDurationMs,
|
|
11208
|
-
args.currentDurationMs
|
|
11209
|
-
),
|
|
11210
|
-
cumulativeUsage: addAgentTurnUsage(
|
|
11211
|
-
latestSessionRecord?.cumulativeUsage,
|
|
11212
|
-
args.currentUsage
|
|
11213
|
-
),
|
|
11214
|
-
...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
|
|
11215
|
-
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
11216
|
-
sessionId: args.sessionId,
|
|
11217
|
-
sliceId: nextSliceId,
|
|
11218
|
-
state: "awaiting_resume",
|
|
11219
|
-
piMessages,
|
|
11220
|
-
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
11221
|
-
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
11222
|
-
resumeReason: "auth",
|
|
11223
|
-
resumedFromSliceId: args.currentSliceId,
|
|
11224
|
-
errorMessage: args.errorMessage,
|
|
11225
|
-
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
11226
|
-
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {}
|
|
11227
|
-
});
|
|
11228
|
-
} catch (recordError) {
|
|
11229
|
-
logSessionRecordError(
|
|
11230
|
-
recordError,
|
|
11231
|
-
"agent_turn_auth_resume_session_record_failed",
|
|
11232
|
-
args,
|
|
11233
|
-
{
|
|
11234
|
-
"app.ai.resume_from_slice_id": args.currentSliceId,
|
|
11235
|
-
"app.ai.resume_next_slice_id": nextSliceId
|
|
11236
|
-
},
|
|
11237
|
-
"Failed to persist auth session record before retry"
|
|
11238
|
-
);
|
|
11239
|
-
}
|
|
11240
|
-
return void 0;
|
|
11241
|
-
}
|
|
11242
|
-
async function persistTimeoutSessionRecord(args) {
|
|
11243
|
-
const nextSliceId = args.currentSliceId + 1;
|
|
11244
|
-
try {
|
|
11245
|
-
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
11246
|
-
args.conversationId,
|
|
11247
|
-
args.sessionId
|
|
11248
|
-
);
|
|
11249
|
-
const piMessages = resumableBoundary(
|
|
11250
|
-
args.messages,
|
|
11251
|
-
latestSessionRecord?.piMessages
|
|
11252
|
-
);
|
|
11253
|
-
if (piMessages.length === 0 || !isContinuableBoundary(piMessages)) {
|
|
11254
|
-
return void 0;
|
|
11255
|
-
}
|
|
11256
|
-
const cumulativeDurationMs = addDurationMs(
|
|
11257
|
-
latestSessionRecord?.cumulativeDurationMs,
|
|
11258
|
-
args.currentDurationMs
|
|
11259
|
-
);
|
|
11260
|
-
const cumulativeUsage = addAgentTurnUsage(
|
|
11261
|
-
latestSessionRecord?.cumulativeUsage,
|
|
11262
|
-
args.currentUsage
|
|
11263
|
-
);
|
|
11264
|
-
if (nextSliceId > AGENT_CONTINUE_MAX_SLICES) {
|
|
11265
|
-
return await upsertAgentTurnSessionRecord({
|
|
11266
|
-
...args.channelName ?? latestSessionRecord?.channelName ? {
|
|
11267
|
-
channelName: args.channelName ?? latestSessionRecord?.channelName
|
|
11268
|
-
} : {},
|
|
11269
|
-
conversationId: args.conversationId,
|
|
11270
|
-
cumulativeDurationMs,
|
|
11271
|
-
cumulativeUsage,
|
|
11272
|
-
...args.destination ?? latestSessionRecord?.destination ? {
|
|
11273
|
-
destination: args.destination ?? latestSessionRecord?.destination
|
|
11274
|
-
} : {},
|
|
11275
|
-
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
11276
|
-
sessionId: args.sessionId,
|
|
11277
|
-
sliceId: args.currentSliceId,
|
|
11278
|
-
state: "failed",
|
|
11279
|
-
piMessages,
|
|
11280
|
-
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
11281
|
-
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
11282
|
-
resumeReason: "timeout",
|
|
11283
|
-
resumedFromSliceId: latestSessionRecord?.resumedFromSliceId,
|
|
11284
|
-
errorMessage: `Agent continuation exceeded slice limit (${AGENT_CONTINUE_MAX_SLICES})`,
|
|
11285
|
-
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
11286
|
-
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {}
|
|
11314
|
+
}
|
|
11315
|
+
async function selectTurnThinkingLevel(args) {
|
|
11316
|
+
const trimmedContext = trimContextForRouter(args.conversationContext);
|
|
11317
|
+
const instructionLength = args.messageText.trim().length;
|
|
11318
|
+
const turnBlockCount = (args.currentTurnBlocks ?? []).filter(
|
|
11319
|
+
(block) => block.trim().length > 0
|
|
11320
|
+
).length;
|
|
11321
|
+
const prompt = buildClassifierPrompt({
|
|
11322
|
+
conversationContext: trimmedContext,
|
|
11323
|
+
currentTurnBlocks: args.currentTurnBlocks,
|
|
11324
|
+
messageText: args.messageText
|
|
11325
|
+
});
|
|
11326
|
+
const logContext = {
|
|
11327
|
+
slackThreadId: args.context?.threadId,
|
|
11328
|
+
slackChannelId: args.context?.channelId,
|
|
11329
|
+
slackUserId: args.context?.requesterId,
|
|
11330
|
+
runId: args.context?.runId,
|
|
11331
|
+
modelId: args.fastModelId
|
|
11332
|
+
};
|
|
11333
|
+
return withSpan(
|
|
11334
|
+
"chat.route_thinking",
|
|
11335
|
+
"chat.route_thinking",
|
|
11336
|
+
logContext,
|
|
11337
|
+
async () => {
|
|
11338
|
+
setSpanAttributes({
|
|
11339
|
+
"app.ai.router.prompt_char_count": prompt.length,
|
|
11340
|
+
"app.ai.router.instruction_char_count": instructionLength,
|
|
11341
|
+
"app.ai.router.context_char_count": trimmedContext?.originalCharCount ?? 0,
|
|
11342
|
+
"app.ai.router.context_trimmed": trimmedContext?.truncated ?? false,
|
|
11343
|
+
"app.ai.router.turn_block_count": turnBlockCount
|
|
11344
|
+
});
|
|
11345
|
+
const selection = await classifyTurn({
|
|
11346
|
+
completeObject: args.completeObject,
|
|
11347
|
+
fastModelId: args.fastModelId,
|
|
11348
|
+
metadata: {
|
|
11349
|
+
modelId: args.fastModelId,
|
|
11350
|
+
threadId: args.context?.threadId ?? "",
|
|
11351
|
+
channelId: args.context?.channelId ?? "",
|
|
11352
|
+
requesterId: args.context?.requesterId ?? "",
|
|
11353
|
+
runId: args.context?.runId ?? ""
|
|
11354
|
+
},
|
|
11355
|
+
prompt
|
|
11356
|
+
});
|
|
11357
|
+
const normalizedSelection = applyThinkingFloor(selection, {
|
|
11358
|
+
minimum: trimmedContext || turnBlockCount > 0 ? "medium" : void 0
|
|
11359
|
+
});
|
|
11360
|
+
setSpanAttributes({
|
|
11361
|
+
"app.ai.thinking_level": normalizedSelection.thinkingLevel,
|
|
11362
|
+
"app.ai.thinking_level_reason": normalizedSelection.reason,
|
|
11363
|
+
...normalizedSelection.confidence !== void 0 ? {
|
|
11364
|
+
"app.ai.thinking_level_confidence": normalizedSelection.confidence
|
|
11365
|
+
} : {}
|
|
11287
11366
|
});
|
|
11367
|
+
return normalizedSelection;
|
|
11288
11368
|
}
|
|
11289
|
-
|
|
11290
|
-
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
|
|
11294
|
-
|
|
11295
|
-
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
11296
|
-
sessionId: args.sessionId,
|
|
11297
|
-
sliceId: nextSliceId,
|
|
11298
|
-
state: "awaiting_resume",
|
|
11299
|
-
piMessages,
|
|
11300
|
-
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
11301
|
-
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
11302
|
-
resumeReason: "timeout",
|
|
11303
|
-
resumedFromSliceId: args.currentSliceId,
|
|
11304
|
-
errorMessage: args.errorMessage,
|
|
11305
|
-
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
11306
|
-
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {}
|
|
11307
|
-
});
|
|
11308
|
-
} catch (recordError) {
|
|
11309
|
-
logSessionRecordError(
|
|
11310
|
-
recordError,
|
|
11311
|
-
"agent_continue_session_record_failed",
|
|
11312
|
-
args,
|
|
11313
|
-
{
|
|
11314
|
-
"app.ai.resume_from_slice_id": args.currentSliceId,
|
|
11315
|
-
"app.ai.resume_next_slice_id": nextSliceId
|
|
11316
|
-
},
|
|
11317
|
-
"Failed to persist session record before scheduling agent continuation"
|
|
11318
|
-
);
|
|
11319
|
-
return void 0;
|
|
11369
|
+
);
|
|
11370
|
+
}
|
|
11371
|
+
function applyThinkingFloor(selection, args) {
|
|
11372
|
+
const minimum = args.minimum;
|
|
11373
|
+
if (!minimum || selection.thinkingLevel === "none" || THINKING_LEVEL_RANK[selection.thinkingLevel] >= THINKING_LEVEL_RANK[minimum]) {
|
|
11374
|
+
return selection;
|
|
11320
11375
|
}
|
|
11376
|
+
return {
|
|
11377
|
+
...selection,
|
|
11378
|
+
thinkingLevel: minimum,
|
|
11379
|
+
reason: `thinking_floor:${minimum}:${selection.reason}`
|
|
11380
|
+
};
|
|
11321
11381
|
}
|
|
11322
|
-
async function
|
|
11382
|
+
async function classifyTurn(args) {
|
|
11323
11383
|
try {
|
|
11324
|
-
const
|
|
11325
|
-
args.
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
args.
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
|
|
11333
|
-
return void 0;
|
|
11334
|
-
}
|
|
11335
|
-
return await upsertAgentTurnSessionRecord({
|
|
11336
|
-
...args.channelName ?? latestSessionRecord?.channelName ? { channelName: args.channelName ?? latestSessionRecord?.channelName } : {},
|
|
11337
|
-
conversationId: args.conversationId,
|
|
11338
|
-
cumulativeDurationMs: addDurationMs(
|
|
11339
|
-
latestSessionRecord?.cumulativeDurationMs,
|
|
11340
|
-
args.currentDurationMs
|
|
11341
|
-
),
|
|
11342
|
-
cumulativeUsage: addAgentTurnUsage(
|
|
11343
|
-
latestSessionRecord?.cumulativeUsage,
|
|
11344
|
-
args.currentUsage
|
|
11345
|
-
),
|
|
11346
|
-
...args.destination ?? latestSessionRecord?.destination ? { destination: args.destination ?? latestSessionRecord?.destination } : {},
|
|
11347
|
-
...args.source ?? latestSessionRecord?.source ? { source: args.source ?? latestSessionRecord?.source } : {},
|
|
11348
|
-
sessionId: args.sessionId,
|
|
11349
|
-
sliceId: args.currentSliceId,
|
|
11350
|
-
state: "awaiting_resume",
|
|
11351
|
-
piMessages,
|
|
11352
|
-
...args.surface ?? latestSessionRecord?.surface ? { surface: args.surface ?? latestSessionRecord?.surface } : {},
|
|
11353
|
-
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
11354
|
-
resumeReason: "yield",
|
|
11355
|
-
resumedFromSliceId: latestSessionRecord?.resumedFromSliceId,
|
|
11356
|
-
errorMessage: args.errorMessage,
|
|
11357
|
-
...args.requester ?? latestSessionRecord?.requester ? { requester: args.requester ?? latestSessionRecord?.requester } : {},
|
|
11358
|
-
...getActiveTraceId() ?? latestSessionRecord?.traceId ? { traceId: getActiveTraceId() ?? latestSessionRecord?.traceId } : {}
|
|
11384
|
+
const result = await args.completeObject({
|
|
11385
|
+
modelId: args.fastModelId,
|
|
11386
|
+
schema: turnExecutionProfileSchema,
|
|
11387
|
+
maxTokens: 120,
|
|
11388
|
+
metadata: args.metadata,
|
|
11389
|
+
prompt: args.prompt,
|
|
11390
|
+
thinkingLevel: "low",
|
|
11391
|
+
system: buildClassifierSystemPrompt(),
|
|
11392
|
+
temperature: 0
|
|
11359
11393
|
});
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
}
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11394
|
+
const parsed = turnExecutionProfileSchema.parse(result.object);
|
|
11395
|
+
const reason = parsed.reason.trim();
|
|
11396
|
+
if (parsed.confidence < CLASSIFIER_CONFIDENCE_THRESHOLD) {
|
|
11397
|
+
return {
|
|
11398
|
+
confidence: parsed.confidence,
|
|
11399
|
+
thinkingLevel: DEFAULT_THINKING_LEVEL,
|
|
11400
|
+
reason: `low_confidence_medium_default:${reason}`
|
|
11401
|
+
};
|
|
11402
|
+
}
|
|
11403
|
+
return {
|
|
11404
|
+
confidence: parsed.confidence,
|
|
11405
|
+
thinkingLevel: parsed.thinking_level,
|
|
11406
|
+
reason
|
|
11407
|
+
};
|
|
11408
|
+
} catch {
|
|
11409
|
+
return {
|
|
11410
|
+
thinkingLevel: DEFAULT_THINKING_LEVEL,
|
|
11411
|
+
reason: "classifier_error_default"
|
|
11412
|
+
};
|
|
11413
|
+
}
|
|
11414
|
+
}
|
|
11415
|
+
function toAgentThinkingLevel(level) {
|
|
11416
|
+
switch (level) {
|
|
11417
|
+
case "none":
|
|
11418
|
+
return "off";
|
|
11419
|
+
case "low":
|
|
11420
|
+
return "low";
|
|
11421
|
+
case "medium":
|
|
11422
|
+
return "medium";
|
|
11423
|
+
case "high":
|
|
11424
|
+
return "high";
|
|
11425
|
+
case "xhigh":
|
|
11426
|
+
return "xhigh";
|
|
11371
11427
|
}
|
|
11372
11428
|
}
|
|
11373
11429
|
|
|
@@ -12824,6 +12880,10 @@ function waitForAbortSettlement(promise, timeoutMs) {
|
|
|
12824
12880
|
}
|
|
12825
12881
|
var startupDiscoveryLogged = false;
|
|
12826
12882
|
var MAX_ROUTER_ATTACHMENT_PREVIEW_CHARS = 2e3;
|
|
12883
|
+
var legacyStoredTextPartSchema = z4.object({
|
|
12884
|
+
text: z4.string(),
|
|
12885
|
+
type: z4.literal("text")
|
|
12886
|
+
}).strict();
|
|
12827
12887
|
function buildOmittedImageAttachmentNotice(count) {
|
|
12828
12888
|
return [
|
|
12829
12889
|
"<omitted-image-attachments>",
|
|
@@ -12988,7 +13048,7 @@ function buildUserTurnInput(args) {
|
|
|
12988
13048
|
}
|
|
12989
13049
|
function buildSteeringPiMessage(message) {
|
|
12990
13050
|
const { userContentParts } = buildUserTurnInput({
|
|
12991
|
-
userTurnText: message.text,
|
|
13051
|
+
userTurnText: buildUserTurnText(message.text),
|
|
12992
13052
|
userAttachments: message.userAttachments,
|
|
12993
13053
|
omittedImageAttachmentCount: message.omittedImageAttachmentCount ?? 0
|
|
12994
13054
|
});
|
|
@@ -13006,15 +13066,52 @@ function withoutTrailingUncheckpointedUserPrompt(messages, userContentParts) {
|
|
|
13006
13066
|
if (lastMessage?.role !== "user") {
|
|
13007
13067
|
return messages;
|
|
13008
13068
|
}
|
|
13009
|
-
|
|
13069
|
+
const comparableLastMessage = stripRuntimeTurnContext([
|
|
13070
|
+
lastMessage
|
|
13071
|
+
])[0];
|
|
13072
|
+
if (!userPromptContentMatches(comparableLastMessage?.content, userContentParts)) {
|
|
13010
13073
|
return messages;
|
|
13011
13074
|
}
|
|
13012
13075
|
return messages.slice(0, -1);
|
|
13013
13076
|
}
|
|
13077
|
+
function userPromptContentMatches(storedContent, currentContent) {
|
|
13078
|
+
if (JSON.stringify(storedContent) === JSON.stringify(currentContent)) {
|
|
13079
|
+
return true;
|
|
13080
|
+
}
|
|
13081
|
+
if (!Array.isArray(storedContent)) {
|
|
13082
|
+
return false;
|
|
13083
|
+
}
|
|
13084
|
+
if (storedContent.length !== currentContent.length) {
|
|
13085
|
+
return false;
|
|
13086
|
+
}
|
|
13087
|
+
return storedContent.every((storedPart, index) => {
|
|
13088
|
+
const currentPart = currentContent[index];
|
|
13089
|
+
if (index === 0 && currentPart?.type === "text") {
|
|
13090
|
+
const legacyTextPart = legacyStoredTextPartSchema.safeParse(storedPart);
|
|
13091
|
+
if (legacyTextPart.success) {
|
|
13092
|
+
return legacyTextPartMatchesCurrentText(
|
|
13093
|
+
legacyTextPart.data.text,
|
|
13094
|
+
currentPart.text
|
|
13095
|
+
);
|
|
13096
|
+
}
|
|
13097
|
+
}
|
|
13098
|
+
return JSON.stringify(storedPart) === JSON.stringify(currentPart);
|
|
13099
|
+
});
|
|
13100
|
+
}
|
|
13101
|
+
function legacyTextPartMatchesCurrentText(storedText, currentText) {
|
|
13102
|
+
const storedInstructionBody = extractCurrentInstructionBody(storedText);
|
|
13103
|
+
if (storedInstructionBody !== void 0) {
|
|
13104
|
+
return renderCurrentInstruction(storedInstructionBody) === currentText;
|
|
13105
|
+
}
|
|
13106
|
+
return renderCurrentInstruction(storedText) === currentText;
|
|
13107
|
+
}
|
|
13014
13108
|
async function generateAssistantReply(messageText2, context) {
|
|
13015
13109
|
const conversationPrivacy = resolveConversationPrivacy({
|
|
13016
13110
|
channelId: context.correlation?.channelId,
|
|
13017
|
-
conversationId: context.correlation?.conversationId ?? context.correlation?.threadId ?? context.correlation?.runId
|
|
13111
|
+
conversationId: context.correlation?.conversationId ?? context.correlation?.threadId ?? context.correlation?.runId,
|
|
13112
|
+
// Source-confirmed visibility from the live event's channel_type; without
|
|
13113
|
+
// it the turn fails closed to private telemetry capture.
|
|
13114
|
+
visibility: context.slackConversation?.visibility
|
|
13018
13115
|
});
|
|
13019
13116
|
return runWithConversationPrivacy(
|
|
13020
13117
|
conversationPrivacy ?? "private",
|
|
@@ -13177,14 +13274,26 @@ async function generateAssistantReplyInPrivacyContext(messageText2, context, con
|
|
|
13177
13274
|
if (!turnSessionState.canUseTurnSession || !sessionConversationId || !sessionId) {
|
|
13178
13275
|
return;
|
|
13179
13276
|
}
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13277
|
+
try {
|
|
13278
|
+
await recordToolExecutionStarted({
|
|
13279
|
+
conversationId: sessionConversationId,
|
|
13280
|
+
sessionId,
|
|
13281
|
+
toolCallId: event.toolCallId,
|
|
13282
|
+
toolName: event.toolName,
|
|
13283
|
+
args: event.args,
|
|
13284
|
+
ttlMs: THREAD_STATE_TTL_MS4
|
|
13285
|
+
});
|
|
13286
|
+
} catch (error) {
|
|
13287
|
+
logException(
|
|
13288
|
+
error,
|
|
13289
|
+
"agent_turn_session_log_append_failed",
|
|
13290
|
+
spanContext,
|
|
13291
|
+
{
|
|
13292
|
+
"gen_ai.tool.name": event.toolName
|
|
13293
|
+
},
|
|
13294
|
+
"Failed to record host-only tool execution start"
|
|
13295
|
+
);
|
|
13296
|
+
}
|
|
13188
13297
|
};
|
|
13189
13298
|
const persistedConfigurationValues = context.channelConfiguration ? await context.channelConfiguration.resolveValues() : {};
|
|
13190
13299
|
configurationValues = {
|
|
@@ -13544,10 +13653,10 @@ async function generateAssistantReplyInPrivacyContext(messageText2, context, con
|
|
|
13544
13653
|
);
|
|
13545
13654
|
const hasPromptCheckpoint = resumedFromSessionRecord && existingSessionRecord?.turnStartMessageIndex !== void 0;
|
|
13546
13655
|
const shouldPromptAgent = !resumedFromSessionRecord || !hasPromptCheckpoint;
|
|
13547
|
-
const promptHistoryMessages = shouldPromptAgent
|
|
13656
|
+
const promptHistoryMessages = shouldPromptAgent ? withoutTrailingUncheckpointedUserPrompt(
|
|
13548
13657
|
priorPiMessages,
|
|
13549
13658
|
userContentParts
|
|
13550
|
-
) :
|
|
13659
|
+
) : existingSessionRecord.piMessages;
|
|
13551
13660
|
const needsBootstrapContextForPrompt = shouldPromptAgent && !hasRuntimeTurnContext(promptHistoryMessages);
|
|
13552
13661
|
const systemPromptContributions = await getPluginSystemPromptContributions(toolSource);
|
|
13553
13662
|
const pluginSystemPrompt = buildPluginSystemPromptContributions(
|
|
@@ -13702,12 +13811,12 @@ async function generateAssistantReplyInPrivacyContext(messageText2, context, con
|
|
|
13702
13811
|
});
|
|
13703
13812
|
if (steeredMessageCount > 0) {
|
|
13704
13813
|
logInfo(
|
|
13705
|
-
"
|
|
13814
|
+
"agent_turn_steering_messages_accepted",
|
|
13706
13815
|
spanContext,
|
|
13707
13816
|
{
|
|
13708
13817
|
"app.ai.steering_message_count": steeredMessageCount
|
|
13709
13818
|
},
|
|
13710
|
-
"Agent turn steering messages
|
|
13819
|
+
"Agent turn steering messages accepted"
|
|
13711
13820
|
);
|
|
13712
13821
|
}
|
|
13713
13822
|
} catch (error) {
|
|
@@ -13798,10 +13907,10 @@ async function generateAssistantReplyInPrivacyContext(messageText2, context, con
|
|
|
13798
13907
|
beforeMessageCount = agent.state.messages.length;
|
|
13799
13908
|
try {
|
|
13800
13909
|
if (resumedFromSessionRecord) {
|
|
13801
|
-
agent.state.messages = shouldPromptAgent ? promptHistoryMessages
|
|
13910
|
+
agent.state.messages = shouldPromptAgent ? promptHistoryMessages : existingSessionRecord.piMessages;
|
|
13802
13911
|
turnStartMessageIndex = existingSessionRecord.turnStartMessageIndex;
|
|
13803
|
-
} else if (
|
|
13804
|
-
agent.state.messages = [...
|
|
13912
|
+
} else if (promptHistoryMessages.length > 0) {
|
|
13913
|
+
agent.state.messages = [...promptHistoryMessages];
|
|
13805
13914
|
}
|
|
13806
13915
|
beforeMessageCount = agent.state.messages.length;
|
|
13807
13916
|
if (shouldPromptAgent) {
|
|
@@ -13969,22 +14078,6 @@ async function generateAssistantReplyInPrivacyContext(messageText2, context, con
|
|
|
13969
14078
|
}
|
|
13970
14079
|
if (turnSessionState.canUseTurnSession && sessionConversationId && sessionId) {
|
|
13971
14080
|
await recordActiveMcpProviders();
|
|
13972
|
-
await persistCompletedSessionRecord({
|
|
13973
|
-
channelName: context.correlation?.channelName,
|
|
13974
|
-
conversationId: sessionConversationId,
|
|
13975
|
-
currentDurationMs: Date.now() - replyStartedAtMs,
|
|
13976
|
-
currentUsage: turnUsage,
|
|
13977
|
-
destination: context.destination,
|
|
13978
|
-
source: runSource,
|
|
13979
|
-
sessionId,
|
|
13980
|
-
sliceId: currentSliceId,
|
|
13981
|
-
allMessages: agent.state.messages,
|
|
13982
|
-
loadedSkillNames: loadedSkillNamesForResume,
|
|
13983
|
-
logContext: sessionRecordLogContext,
|
|
13984
|
-
requester,
|
|
13985
|
-
...surface ? { surface } : {},
|
|
13986
|
-
...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {}
|
|
13987
|
-
});
|
|
13988
14081
|
}
|
|
13989
14082
|
return buildTurnResult({
|
|
13990
14083
|
newMessages,
|
|
@@ -14146,7 +14239,7 @@ async function generateAssistantReplyInPrivacyContext(messageText2, context, con
|
|
|
14146
14239
|
);
|
|
14147
14240
|
const message = error instanceof Error ? error.message : String(error);
|
|
14148
14241
|
return {
|
|
14149
|
-
text:
|
|
14242
|
+
text: "",
|
|
14150
14243
|
...getSandboxMetadata(),
|
|
14151
14244
|
diagnostics: {
|
|
14152
14245
|
outcome: "provider_error",
|
|
@@ -14537,6 +14630,98 @@ function getConversationMessageSlackTs(message) {
|
|
|
14537
14630
|
return message.meta?.slackTs ?? toOptionalString(message.id);
|
|
14538
14631
|
}
|
|
14539
14632
|
|
|
14633
|
+
// src/chat/services/turn-failure-response.ts
|
|
14634
|
+
function requireTurnFailureEventId(eventId, eventName) {
|
|
14635
|
+
if (!eventId) {
|
|
14636
|
+
throw new Error(`Sentry did not return an event ID for ${eventName}`);
|
|
14637
|
+
}
|
|
14638
|
+
return eventId;
|
|
14639
|
+
}
|
|
14640
|
+
function getExecutionFailureReason(reply) {
|
|
14641
|
+
const errorMessage = reply.diagnostics.errorMessage?.trim();
|
|
14642
|
+
if (errorMessage) {
|
|
14643
|
+
return errorMessage;
|
|
14644
|
+
}
|
|
14645
|
+
if (reply.diagnostics.toolErrorCount > 0) {
|
|
14646
|
+
return `${reply.diagnostics.toolErrorCount} tool result error(s)`;
|
|
14647
|
+
}
|
|
14648
|
+
if (reply.diagnostics.assistantMessageCount > 0) {
|
|
14649
|
+
return "assistant returned no text";
|
|
14650
|
+
}
|
|
14651
|
+
return "empty assistant turn";
|
|
14652
|
+
}
|
|
14653
|
+
function getFailureCapture(reply) {
|
|
14654
|
+
if (reply.diagnostics.outcome === "provider_error") {
|
|
14655
|
+
return {
|
|
14656
|
+
eventName: "agent_turn_provider_error",
|
|
14657
|
+
error: reply.diagnostics.providerError ?? new Error(
|
|
14658
|
+
reply.diagnostics.errorMessage ?? "Provider error without explicit message"
|
|
14659
|
+
),
|
|
14660
|
+
attributes: {},
|
|
14661
|
+
body: "Agent turn failed with provider error"
|
|
14662
|
+
};
|
|
14663
|
+
}
|
|
14664
|
+
const failureReason = getExecutionFailureReason(reply);
|
|
14665
|
+
return {
|
|
14666
|
+
eventName: "agent_turn_execution_failure",
|
|
14667
|
+
error: new Error(`Agent turn execution failure: ${failureReason}`),
|
|
14668
|
+
attributes: {
|
|
14669
|
+
"app.ai.execution_failure_reason": failureReason
|
|
14670
|
+
},
|
|
14671
|
+
body: "Agent turn completed with execution failure"
|
|
14672
|
+
};
|
|
14673
|
+
}
|
|
14674
|
+
function getAgentTurnDiagnosticsAttributes(reply) {
|
|
14675
|
+
return {
|
|
14676
|
+
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
14677
|
+
"gen_ai.operation.name": "invoke_agent",
|
|
14678
|
+
"app.ai.outcome": reply.diagnostics.outcome,
|
|
14679
|
+
"app.ai.assistant_messages": reply.diagnostics.assistantMessageCount,
|
|
14680
|
+
"app.ai.tool_results": reply.diagnostics.toolResultCount,
|
|
14681
|
+
"app.ai.tool_error_results": reply.diagnostics.toolErrorCount,
|
|
14682
|
+
"app.ai.tool_call_count": reply.diagnostics.toolCalls.length,
|
|
14683
|
+
"app.ai.used_primary_text": reply.diagnostics.usedPrimaryText,
|
|
14684
|
+
...reply.diagnostics.thinkingLevel ? {
|
|
14685
|
+
"app.ai.reasoning_effort": reply.diagnostics.thinkingLevel
|
|
14686
|
+
} : {},
|
|
14687
|
+
...reply.diagnostics.stopReason ? {
|
|
14688
|
+
"gen_ai.response.finish_reasons": [reply.diagnostics.stopReason]
|
|
14689
|
+
} : {},
|
|
14690
|
+
...reply.diagnostics.errorMessage ? { "exception.message": reply.diagnostics.errorMessage } : {}
|
|
14691
|
+
};
|
|
14692
|
+
}
|
|
14693
|
+
function finalizeFailedTurnReply(args) {
|
|
14694
|
+
if (args.reply.diagnostics.outcome === "success") {
|
|
14695
|
+
return args.reply;
|
|
14696
|
+
}
|
|
14697
|
+
const capture = getFailureCapture(args.reply);
|
|
14698
|
+
const eventId = requireTurnFailureEventId(
|
|
14699
|
+
args.logException(
|
|
14700
|
+
capture.error,
|
|
14701
|
+
capture.eventName,
|
|
14702
|
+
args.context,
|
|
14703
|
+
{
|
|
14704
|
+
...getAgentTurnDiagnosticsAttributes(args.reply),
|
|
14705
|
+
...args.attributes,
|
|
14706
|
+
...capture.attributes
|
|
14707
|
+
},
|
|
14708
|
+
capture.body
|
|
14709
|
+
),
|
|
14710
|
+
capture.eventName
|
|
14711
|
+
);
|
|
14712
|
+
const providerPartialText = args.reply.diagnostics.outcome === "provider_error" && args.reply.diagnostics.assistantMessageCount > 0 ? args.reply.text.trim() : "";
|
|
14713
|
+
return {
|
|
14714
|
+
...args.reply,
|
|
14715
|
+
text: providerPartialText ? `${providerPartialText}${getInterruptionMarker()}` : buildTurnFailureResponse(eventId),
|
|
14716
|
+
deliveryMode: "thread",
|
|
14717
|
+
deliveryPlan: {
|
|
14718
|
+
mode: "thread",
|
|
14719
|
+
postThreadText: true,
|
|
14720
|
+
attachFiles: args.reply.files && args.reply.files.length > 0 ? "inline" : "none"
|
|
14721
|
+
}
|
|
14722
|
+
};
|
|
14723
|
+
}
|
|
14724
|
+
|
|
14540
14725
|
// src/chat/plugins/task-runner.ts
|
|
14541
14726
|
import { pluginRunContextSchema } from "@sentry/junior-plugin-api";
|
|
14542
14727
|
var PLUGIN_TASK_LOCK_TTL_MS = 5 * 60 * 1e3;
|
|
@@ -14812,13 +14997,19 @@ export {
|
|
|
14812
14997
|
isCooperativeTurnYieldError,
|
|
14813
14998
|
TurnInputCommitLostError,
|
|
14814
14999
|
isTurnInputCommitLostError,
|
|
15000
|
+
TurnInputDeferredError,
|
|
15001
|
+
isTurnInputDeferredError,
|
|
14815
15002
|
startActiveTurn,
|
|
14816
15003
|
markTurnClosed,
|
|
14817
15004
|
markTurnFailed,
|
|
14818
15005
|
isRedundantReactionAckText,
|
|
15006
|
+
persistCompletedSessionRecord,
|
|
15007
|
+
completeDeliveredTurn,
|
|
15008
|
+
persistYieldSessionRecord,
|
|
14819
15009
|
finalizeMcpAuthorization,
|
|
14820
15010
|
resolvePluginOAuthAccount,
|
|
14821
15011
|
executeCredentialedEgressRequest,
|
|
15012
|
+
buildSteeringPiMessage,
|
|
14822
15013
|
generateAssistantReply,
|
|
14823
15014
|
estimateTextTokens,
|
|
14824
15015
|
getAgentContextCompactionTriggerTokens,
|
|
@@ -14832,6 +15023,9 @@ export {
|
|
|
14832
15023
|
createConversationMemoryService,
|
|
14833
15024
|
isHumanConversationMessage,
|
|
14834
15025
|
getConversationMessageSlackTs,
|
|
15026
|
+
requireTurnFailureEventId,
|
|
15027
|
+
getAgentTurnDiagnosticsAttributes,
|
|
15028
|
+
finalizeFailedTurnReply,
|
|
14835
15029
|
scheduleSessionCompletedPluginTasks,
|
|
14836
15030
|
processPluginTask,
|
|
14837
15031
|
buildDeliveredTurnStatePatch
|