@sentry/junior 0.96.0 → 0.97.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-5URSMY3O.js → agent-hooks-XSUIVRQS.js} +8 -6
- package/dist/{reporting → api}/conversations/activity.d.ts +1 -1
- package/dist/api/conversations/detail-projection.d.ts +10 -0
- package/dist/api/conversations/detail.d.ts +3 -0
- package/dist/api/conversations/detail.query.d.ts +3 -0
- package/dist/api/conversations/list.d.ts +3 -0
- package/dist/api/conversations/list.query.d.ts +94 -0
- package/dist/api/conversations/projection.d.ts +14 -0
- package/dist/api/conversations/schema.d.ts +683 -0
- package/dist/{reporting → api}/conversations/shared.d.ts +1 -5
- package/dist/api/conversations/stats.d.ts +3 -0
- package/dist/api/conversations/stats.query.d.ts +3 -0
- package/dist/api/conversations/subagent.d.ts +3 -0
- package/dist/{reporting → api}/conversations/transcript.d.ts +4 -4
- package/dist/api/people/list.d.ts +1 -2
- package/dist/api/people/list.query.d.ts +3 -4
- package/dist/api/people/profile.d.ts +1 -2
- package/dist/api/people/profile.query.d.ts +3 -4
- package/dist/api/people/schema.d.ts +193 -0
- package/dist/api/people/shared.d.ts +31 -9
- package/dist/api/schema.d.ts +20 -0
- package/dist/api/schema.js +50 -0
- package/dist/api-reference.d.ts +4 -3
- package/dist/api.d.ts +3 -0
- package/dist/api.js +156 -0
- package/dist/app.d.ts +1 -4
- package/dist/app.js +100 -366
- package/dist/build/virtual-config.d.ts +2 -2
- package/dist/{catalog-runtime-MSMAYYIK.js → catalog-runtime-PFHVMHDR.js} +3 -2
- package/dist/chat/agent/resume.d.ts +3 -1
- package/dist/chat/agent/tools.d.ts +2 -1
- package/dist/chat/config.d.ts +1 -8
- package/dist/chat/conversations/history.d.ts +241 -27
- package/dist/chat/conversations/legacy-advisor-session.d.ts +7 -0
- package/dist/chat/conversations/legacy-import.d.ts +2 -2
- package/dist/chat/conversations/projection.d.ts +19 -3
- package/dist/chat/conversations/sql/legacy-history-import.d.ts +1 -1
- package/dist/chat/conversations/sql/store.d.ts +15 -9
- package/dist/chat/conversations/store.d.ts +9 -4
- package/dist/chat/model-profile.d.ts +14 -0
- package/dist/chat/pi/transcript.d.ts +5 -3
- package/dist/chat/resource-events/store.d.ts +2 -2
- package/dist/chat/services/context-compaction.d.ts +15 -0
- package/dist/chat/services/turn-result.d.ts +1 -0
- package/dist/chat/services/turn-session-record.d.ts +6 -6
- package/dist/chat/state/turn-session.d.ts +16 -5
- package/dist/chat/task-execution/store.d.ts +2 -2
- package/dist/chat/tools/handoff/tool.d.ts +45 -0
- package/dist/chat/tools/types.d.ts +7 -2
- package/dist/chat/usage.d.ts +25 -25
- package/dist/{chunk-RITD7E5L.js → chunk-35BNLDOJ.js} +338 -577
- package/dist/{chunk-PMNN6ST4.js → chunk-3ECW2K2O.js} +7 -2
- package/dist/{chunk-5C6XTTFI.js → chunk-3F4OPKGF.js} +89 -55
- package/dist/chunk-3V7OFJZC.js +264 -0
- package/dist/chunk-4ZNGQH7C.js +25 -0
- package/dist/{chunk-S5KZ5LPG.js → chunk-5HLDPVYS.js} +14 -447
- package/dist/{chunk-DZENG5ML.js → chunk-6U26PYI5.js} +24 -14
- package/dist/chunk-AUUCAFDE.js +546 -0
- package/dist/{chunk-6APU57E6.js → chunk-B3IT5END.js} +41 -260
- package/dist/chunk-CEA3A3UA.js +86 -0
- package/dist/{chunk-YDT7SNCN.js → chunk-D5KZYS4T.js} +1 -1
- package/dist/{chunk-YDVEH3SB.js → chunk-EEZCY5WX.js} +78 -28
- package/dist/{chunk-2NAZZPPS.js → chunk-EOWXRY5E.js} +1 -1
- package/dist/{chunk-GQ3GK22G.js → chunk-FBHZJFHI.js} +2 -2
- package/dist/{chunk-CZEX6KIQ.js → chunk-G3E7SCME.js} +0 -23
- package/dist/chunk-GUAP3H7O.js +319 -0
- package/dist/{chunk-BYZWPRJA.js → chunk-H66UCBPW.js} +6 -4
- package/dist/{chunk-L7CDEIVJ.js → chunk-LMJPRYHP.js} +4 -2
- package/dist/chunk-LUAE4MGW.js +199 -0
- package/dist/{chunk-GY34VGP5.js → chunk-LVA4I4H7.js} +3 -1
- package/dist/chunk-LVUKF5CE.js +63 -0
- package/dist/{chunk-AAZXTVJT.js → chunk-NG2AV7IB.js} +2 -72
- package/dist/chunk-ON6YKIQ7.js +260 -0
- package/dist/chunk-PDO5BLNM.js +22 -0
- package/dist/chunk-QNVHH5PU.js +20 -0
- package/dist/{chunk-P3YFQLEN.js → chunk-T533JLM3.js} +8 -10
- package/dist/chunk-UAMKJPDF.js +11 -0
- package/dist/chunk-VFH4ZKZT.js +66 -0
- package/dist/{chunk-UKHVMPX5.js → chunk-WGXMTFNU.js} +15 -17
- package/dist/{chunk-MNTINMWR.js → chunk-WJOSZOON.js} +184 -300
- package/dist/chunk-WTYEKPXU.js +451 -0
- package/dist/{chunk-IYM2SIVG.js → chunk-XIRPQQVE.js} +605 -363
- package/dist/cli/chat.js +24 -21
- package/dist/cli/check.js +4 -3
- package/dist/cli/init.js +12 -0
- package/dist/cli/plugins.js +12 -9
- package/dist/cli/snapshot-warmup.js +8 -6
- package/dist/cli/upgrade.js +265 -137
- package/dist/db/db.d.ts +4 -0
- package/dist/db/schema/conversations.d.ts +128 -0
- package/dist/db/schema.d.ts +129 -60
- package/dist/{db-2D6RMG67.js → db-BWXYIKFQ.js} +3 -3
- package/dist/detail-GA6NXWQJ.js +43 -0
- package/dist/discovery-RMHYSRL7.js +43 -0
- package/dist/handlers/health.d.ts +3 -0
- package/dist/instrumentation.js +1 -1
- package/dist/{legacy-import-VZ2MEGP7.js → legacy-import-3NQJVGQL.js} +4 -4
- package/dist/{api/people/list.js → list-A3WFRIYX.js} +26 -16
- package/dist/list-UXJI5Q5X.js +27 -0
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +2 -1
- package/dist/plugin-conversations-DDNGC35A.js +36 -0
- package/dist/{api/people/profile.js → profile-4RYDXWTF.js} +33 -18
- package/dist/reporting/plugin-conversations.d.ts +3 -0
- package/dist/reporting-schema.d.ts +140 -0
- package/dist/reporting.d.ts +12 -81
- package/dist/{runner-JZBRQP4F.js → runner-JALSYH2P.js} +17 -12
- package/dist/skills-MAZXZ2KE.js +26 -0
- package/dist/stats-XKXH3PTY.js +217 -0
- package/dist/subagent-TZ55XY37.js +48 -0
- package/dist/usage-schema.d.ts +25 -0
- package/dist/{validation-6XOOQGME.js → validation-FMEAEKCY.js} +4 -3
- package/migrations/0000_initial.sql +115 -0
- package/migrations/0001_conversation_metrics.sql +4 -0
- package/migrations/README.md +12 -30
- package/migrations/meta/0000_snapshot.json +1 -33
- package/migrations/meta/0001_snapshot.json +972 -0
- package/migrations/meta/_journal.json +9 -2
- package/package.json +14 -18
- package/dist/api/people/types.d.ts +0 -85
- package/dist/chat/advisor-request.d.ts +0 -4
- package/dist/chat/conversations/sql/migrations.d.ts +0 -22
- package/dist/chat/tools/advisor/session-store.d.ts +0 -9
- package/dist/chat/tools/advisor/tool.d.ts +0 -72
- package/dist/chunk-RAIB6WSG.js +0 -142
- package/dist/chunk-WM6KHPBQ.js +0 -48
- package/dist/db/schema/migrations.d.ts +0 -58
- package/dist/reporting/conversations/context.d.ts +0 -8
- package/dist/reporting/conversations/details.d.ts +0 -6
- package/dist/reporting/conversations/index.d.ts +0 -4
- package/dist/reporting/conversations/statistics.d.ts +0 -4
- package/dist/reporting/conversations/summaries.d.ts +0 -20
- package/dist/reporting/conversations/types.d.ts +0 -191
- package/dist/reporting/conversations.d.ts +0 -1
- package/dist/reporting.js +0 -1155
|
@@ -10,7 +10,25 @@ import {
|
|
|
10
10
|
persistRunningSessionRecord,
|
|
11
11
|
persistTimeoutSessionRecord,
|
|
12
12
|
persistYieldSessionRecord
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WGXMTFNU.js";
|
|
14
|
+
import {
|
|
15
|
+
JUNIOR_THREAD_STATE_TTL_MS,
|
|
16
|
+
addAgentTurnUsage,
|
|
17
|
+
hasAgentTurnUsage
|
|
18
|
+
} from "./chunk-WJOSZOON.js";
|
|
19
|
+
import {
|
|
20
|
+
loadConnectedMcpProviders,
|
|
21
|
+
loadConversationProjection,
|
|
22
|
+
openConversationProjection,
|
|
23
|
+
recordAuthorizationRequested,
|
|
24
|
+
recordMcpProviderConnected,
|
|
25
|
+
recordToolExecutionStarted
|
|
26
|
+
} from "./chunk-GUAP3H7O.js";
|
|
27
|
+
import {
|
|
28
|
+
estimateTextTokens,
|
|
29
|
+
getAgentContextCompactionTriggerTokens,
|
|
30
|
+
updateConversationStats
|
|
31
|
+
} from "./chunk-3F4OPKGF.js";
|
|
14
32
|
import {
|
|
15
33
|
buildNonInteractiveShellScript,
|
|
16
34
|
createSandboxInstance,
|
|
@@ -20,66 +38,58 @@ import {
|
|
|
20
38
|
isSnapshotMissingError,
|
|
21
39
|
resolveRuntimeDependencySnapshot,
|
|
22
40
|
runNonInteractiveCommand
|
|
23
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-H66UCBPW.js";
|
|
24
42
|
import {
|
|
25
43
|
privateTraceResultAttributes
|
|
26
44
|
} from "./chunk-CM5EZD5F.js";
|
|
27
45
|
import {
|
|
28
|
-
renderAdvisorRequest
|
|
29
|
-
} from "./chunk-WM6KHPBQ.js";
|
|
30
|
-
import {
|
|
31
|
-
addAgentTurnUsage,
|
|
32
|
-
commitMessages,
|
|
33
|
-
hasAgentTurnUsage,
|
|
34
|
-
loadConnectedMcpProviders,
|
|
35
|
-
loadProjection,
|
|
36
|
-
recordAuthorizationRequested,
|
|
37
|
-
recordMcpProviderConnected,
|
|
38
|
-
recordToolExecutionStarted
|
|
39
|
-
} from "./chunk-MNTINMWR.js";
|
|
40
|
-
import {
|
|
41
|
-
JUNIOR_THREAD_STATE_TTL_MS
|
|
42
|
-
} from "./chunk-5C6XTTFI.js";
|
|
43
|
-
import {
|
|
44
|
-
SlackActionError,
|
|
45
46
|
createPluginHookRunner,
|
|
46
|
-
downloadPrivateSlackFile,
|
|
47
|
-
getFilePermalink,
|
|
48
47
|
getPluginSystemPromptContributions,
|
|
49
48
|
getPluginTools,
|
|
50
49
|
getPluginUserPromptContributions,
|
|
51
50
|
getPlugins,
|
|
52
|
-
getSlackClient,
|
|
53
51
|
getSlackToolContext,
|
|
52
|
+
resolveChannelCapabilities
|
|
53
|
+
} from "./chunk-5HLDPVYS.js";
|
|
54
|
+
import {
|
|
55
|
+
SlackActionError,
|
|
56
|
+
downloadPrivateSlackFile,
|
|
57
|
+
getFilePermalink,
|
|
58
|
+
getSlackClient,
|
|
54
59
|
isConversationScopedChannel,
|
|
55
60
|
isDmChannel,
|
|
56
61
|
normalizeSlackConversationId,
|
|
57
|
-
resolveChannelCapabilities,
|
|
58
62
|
withSlackRetries
|
|
59
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-WTYEKPXU.js";
|
|
60
64
|
import {
|
|
61
65
|
createPluginLogger
|
|
62
|
-
} from "./chunk-
|
|
63
|
-
import {
|
|
64
|
-
SANDBOX_DATA_ROOT,
|
|
65
|
-
SANDBOX_SKILLS_ROOT,
|
|
66
|
-
SANDBOX_WORKSPACE_ROOT,
|
|
67
|
-
sandboxSkillDir,
|
|
68
|
-
sandboxSkillFile,
|
|
69
|
-
sleep
|
|
70
|
-
} from "./chunk-CZEX6KIQ.js";
|
|
66
|
+
} from "./chunk-EOWXRY5E.js";
|
|
71
67
|
import {
|
|
68
|
+
contextProvenance,
|
|
72
69
|
getAgentStepStore,
|
|
73
70
|
getConversationStore,
|
|
74
71
|
getDb,
|
|
75
72
|
instructionActors,
|
|
76
73
|
instructionProvenanceFor
|
|
77
|
-
} from "./chunk-
|
|
74
|
+
} from "./chunk-35BNLDOJ.js";
|
|
78
75
|
import {
|
|
76
|
+
SANDBOX_DATA_ROOT,
|
|
77
|
+
SANDBOX_SKILLS_ROOT,
|
|
78
|
+
SANDBOX_WORKSPACE_ROOT,
|
|
79
|
+
sandboxSkillDir,
|
|
80
|
+
sandboxSkillFile
|
|
81
|
+
} from "./chunk-G3E7SCME.js";
|
|
82
|
+
import {
|
|
83
|
+
sleep
|
|
84
|
+
} from "./chunk-4ZNGQH7C.js";
|
|
85
|
+
import {
|
|
86
|
+
DEFAULT_HANDOFF_MODEL_PROFILE,
|
|
79
87
|
GEN_AI_PROVIDER_NAME,
|
|
80
88
|
GEN_AI_SERVER_ADDRESS,
|
|
81
89
|
GEN_AI_SERVER_PORT,
|
|
82
90
|
MISSING_GATEWAY_CREDENTIALS_ERROR,
|
|
91
|
+
ModelProfileNotConfiguredError,
|
|
92
|
+
STANDARD_MODEL_PROFILE,
|
|
83
93
|
TURN_CONTEXT_TAG,
|
|
84
94
|
acquireActiveLock,
|
|
85
95
|
botConfig,
|
|
@@ -96,13 +106,17 @@ import {
|
|
|
96
106
|
isProviderRetryError,
|
|
97
107
|
isToolResultError,
|
|
98
108
|
isToolResultMessage,
|
|
109
|
+
modelIdForProfile,
|
|
99
110
|
nextProviderRetry,
|
|
100
111
|
normalizeSlackEmojiName,
|
|
101
112
|
normalizeToolNameFromResult,
|
|
102
113
|
renderCurrentInstruction,
|
|
103
114
|
resolveGatewayModel,
|
|
104
|
-
|
|
105
|
-
|
|
115
|
+
retainRuntimeTurnContext,
|
|
116
|
+
stripRuntimeTurnContext,
|
|
117
|
+
trimTrailingAssistantMessages,
|
|
118
|
+
unwrapCurrentInstruction
|
|
119
|
+
} from "./chunk-EEZCY5WX.js";
|
|
106
120
|
import {
|
|
107
121
|
parseSlackThreadId,
|
|
108
122
|
resolveConversationPrivacy,
|
|
@@ -114,7 +128,7 @@ import {
|
|
|
114
128
|
toGenAiPayloadMetadata,
|
|
115
129
|
toGenAiPayloadTraceAttributes,
|
|
116
130
|
toGenAiTextMetadata
|
|
117
|
-
} from "./chunk-
|
|
131
|
+
} from "./chunk-LVA4I4H7.js";
|
|
118
132
|
import {
|
|
119
133
|
parseDestination
|
|
120
134
|
} from "./chunk-B7PUBQQM.js";
|
|
@@ -123,14 +137,14 @@ import {
|
|
|
123
137
|
findSkillByName,
|
|
124
138
|
loadSkillsByName,
|
|
125
139
|
parseSkillInvocation
|
|
126
|
-
} from "./chunk-
|
|
140
|
+
} from "./chunk-3ECW2K2O.js";
|
|
127
141
|
import {
|
|
128
142
|
CredentialUnavailableError,
|
|
129
143
|
credentialContextSchema,
|
|
130
144
|
pluginCatalogRuntime,
|
|
131
145
|
resolveAuthTokenPlaceholder,
|
|
132
146
|
resolvePluginCommandEnv
|
|
133
|
-
} from "./chunk-
|
|
147
|
+
} from "./chunk-LMJPRYHP.js";
|
|
134
148
|
import {
|
|
135
149
|
createActor,
|
|
136
150
|
extractGenAiUsageAttributes,
|
|
@@ -157,16 +171,16 @@ import {
|
|
|
157
171
|
startInactiveSpan,
|
|
158
172
|
withActiveSpan
|
|
159
173
|
} from "./chunk-6GWA276C.js";
|
|
160
|
-
import {
|
|
161
|
-
listReferenceFiles,
|
|
162
|
-
soulPathCandidates,
|
|
163
|
-
worldPathCandidates
|
|
164
|
-
} from "./chunk-6APU57E6.js";
|
|
165
174
|
import {
|
|
166
175
|
parseSlackChannelId,
|
|
167
176
|
parseSlackMessageTs,
|
|
168
177
|
parseSlackUserId
|
|
169
178
|
} from "./chunk-7FBGKXPE.js";
|
|
179
|
+
import {
|
|
180
|
+
listReferenceFiles,
|
|
181
|
+
soulPathCandidates,
|
|
182
|
+
worldPathCandidates
|
|
183
|
+
} from "./chunk-3V7OFJZC.js";
|
|
170
184
|
|
|
171
185
|
// src/chat/configuration/defaults.ts
|
|
172
186
|
var installDefaults = {};
|
|
@@ -249,7 +263,7 @@ function shouldPropagateSandboxEgressTrace(host, config = {}) {
|
|
|
249
263
|
}
|
|
250
264
|
|
|
251
265
|
// src/chat/agent/index.ts
|
|
252
|
-
import { Agent
|
|
266
|
+
import { Agent } from "@earendil-works/pi-agent-core";
|
|
253
267
|
|
|
254
268
|
// src/chat/sandbox/skill-sandbox.ts
|
|
255
269
|
import fs from "fs/promises";
|
|
@@ -672,7 +686,8 @@ function buildTurnResult(input) {
|
|
|
672
686
|
usage,
|
|
673
687
|
thinkingSelection,
|
|
674
688
|
correlation,
|
|
675
|
-
assistantUserName
|
|
689
|
+
assistantUserName,
|
|
690
|
+
modelId
|
|
676
691
|
} = input;
|
|
677
692
|
const toolResults = newMessages.filter(isToolResultMessage);
|
|
678
693
|
const assistantMessages = newMessages.filter(isAssistantMessage);
|
|
@@ -709,7 +724,7 @@ function buildTurnResult(input) {
|
|
|
709
724
|
slackChannelId: correlation?.channelId,
|
|
710
725
|
runId: correlation?.runId,
|
|
711
726
|
assistantUserName,
|
|
712
|
-
modelId
|
|
727
|
+
modelId
|
|
713
728
|
};
|
|
714
729
|
const markerAttributes = {
|
|
715
730
|
"app.ai.no_reply_marker": true,
|
|
@@ -733,7 +748,7 @@ function buildTurnResult(input) {
|
|
|
733
748
|
slackChannelId: correlation?.channelId,
|
|
734
749
|
runId: correlation?.runId,
|
|
735
750
|
assistantUserName,
|
|
736
|
-
modelId
|
|
751
|
+
modelId
|
|
737
752
|
},
|
|
738
753
|
{
|
|
739
754
|
"app.ai.no_reply_marker": true,
|
|
@@ -751,7 +766,7 @@ function buildTurnResult(input) {
|
|
|
751
766
|
slackChannelId: correlation?.channelId,
|
|
752
767
|
runId: correlation?.runId,
|
|
753
768
|
assistantUserName,
|
|
754
|
-
modelId
|
|
769
|
+
modelId
|
|
755
770
|
},
|
|
756
771
|
{
|
|
757
772
|
"app.ai.tool_results": toolResults.length,
|
|
@@ -797,7 +812,7 @@ function buildTurnResult(input) {
|
|
|
797
812
|
}
|
|
798
813
|
const resolvedDiagnostics = {
|
|
799
814
|
outcome: resolvedOutcome,
|
|
800
|
-
modelId
|
|
815
|
+
modelId,
|
|
801
816
|
assistantMessageCount: assistantMessages.length,
|
|
802
817
|
thinkingLevel: thinkingSelection.thinkingLevel,
|
|
803
818
|
toolCalls,
|
|
@@ -2031,6 +2046,7 @@ var LOCAL_HEADER = "You are a helper assistant. Follow the personality section f
|
|
|
2031
2046
|
var TURN_CONTEXT_HEADER = "Runtime context for this request. Treat these blocks as trusted runtime facts; the static system prompt remains authoritative.";
|
|
2032
2047
|
var TOOL_POLICY_RULES = [
|
|
2033
2048
|
"- Tool schemas are the source of truth for parameters; tool names are case-sensitive, so call tools exactly by their exposed names and do not invent arguments.",
|
|
2049
|
+
"- Model upgrade: when `handoff` is available, call it immediately and as the only tool before handling any request to write, edit, review, debug, or substantially reason about code; make a multi-file change; perform architecture or root-cause analysis; do research-heavy synthesis or complex planning; or complete another task where a more capable model materially improves reliability. Choose the configured profile whose name best fits the task, or omit `profile` to use the default. Do not inspect or modify files first. After handoff, continue the same task normally.",
|
|
2034
2050
|
"- Use tools for actionable work and for facts that are mutable, external, repository-backed, provider-backed, or requested as verified/current. Stable general knowledge and already-provided context may be answered directly.",
|
|
2035
2051
|
"- Resolve provider action targets before calls: explicit target wins; ambient `<configuration>` fills omitted targets. Treat non-target links/references as context.",
|
|
2036
2052
|
"- Verification source order: conversation/thread context; user-provided attachments, links, and reference files; local/sandbox files when present; loaded skill references; repository/provider tools; public web. Use the nearest authoritative available source before weaker sources.",
|
|
@@ -8367,252 +8383,33 @@ function createSystemTimeTool() {
|
|
|
8367
8383
|
});
|
|
8368
8384
|
}
|
|
8369
8385
|
|
|
8370
|
-
// src/chat/tools/
|
|
8371
|
-
import {
|
|
8372
|
-
Agent
|
|
8373
|
-
} from "@earendil-works/pi-agent-core";
|
|
8386
|
+
// src/chat/tools/handoff/tool.ts
|
|
8374
8387
|
import { z as z33 } from "zod";
|
|
8375
|
-
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8381
|
-
|
|
8382
|
-
|
|
8383
|
-
"Do not assume access to parent transcript or tool output that was not included or gathered in this advisor call.",
|
|
8384
|
-
"Use only the read-only tools provided to you.",
|
|
8385
|
-
"Do not make user-visible side effects, post Slack messages, or mutate files. If a mutating action is needed, recommend it to the executor instead.",
|
|
8386
|
-
"Identify the hard part, recommend a concrete plan or correction, call out blocking risks, and propose focused verification.",
|
|
8387
|
-
"If the supplied context is insufficient, say exactly what additional evidence the executor needs to gather before acting.",
|
|
8388
|
-
"Do not write user-facing prose.",
|
|
8389
|
-
"Use concise technical memo sections when helpful: Assessment, Recommended Plan, Risks, Verification, Stop Conditions."
|
|
8390
|
-
].join("\n");
|
|
8391
|
-
function lastAssistantMessage(messages) {
|
|
8392
|
-
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
8393
|
-
const message = messages[index];
|
|
8394
|
-
if (isAssistantMessage(message)) {
|
|
8395
|
-
return message;
|
|
8396
|
-
}
|
|
8397
|
-
}
|
|
8398
|
-
return void 0;
|
|
8399
|
-
}
|
|
8400
|
-
function failure(errorCode, text = `Advisor guidance is unavailable (${errorCode}). Continue only if the next step is clear from verified evidence.`) {
|
|
8401
|
-
return {
|
|
8402
|
-
ok: false,
|
|
8403
|
-
status: "error",
|
|
8404
|
-
error_code: errorCode,
|
|
8405
|
-
memo: text
|
|
8406
|
-
};
|
|
8407
|
-
}
|
|
8408
|
-
function success(memo) {
|
|
8409
|
-
return {
|
|
8410
|
-
ok: true,
|
|
8411
|
-
status: "success",
|
|
8412
|
-
memo
|
|
8413
|
-
};
|
|
8414
|
-
}
|
|
8415
|
-
function hasReadOnlyToolAnnotations(annotations) {
|
|
8416
|
-
return annotations?.readOnlyHint === true && annotations.destructiveHint !== true;
|
|
8417
|
-
}
|
|
8418
|
-
function createAdvisorToolDefinitions(definitions) {
|
|
8419
|
-
return Object.fromEntries(
|
|
8420
|
-
Object.entries(definitions).filter(
|
|
8421
|
-
([name, definition]) => name !== "callMcpTool" && name !== "searchMcpTools" && hasReadOnlyToolAnnotations(definition.annotations)
|
|
8422
|
-
)
|
|
8423
|
-
);
|
|
8424
|
-
}
|
|
8425
|
-
function createAdvisorTool(context) {
|
|
8426
|
-
const conversationStore = () => context.conversationStore ?? getConversationStore();
|
|
8427
|
-
const spanContext = context.logContext ?? {};
|
|
8388
|
+
var HANDOFF_TOOL_NAME = "handoff";
|
|
8389
|
+
function createHandoffTool(handoff) {
|
|
8390
|
+
const profileSchema = z33.enum(handoff.profiles);
|
|
8391
|
+
const defaultProfile = handoff.profiles[0];
|
|
8392
|
+
const handoffResultSchema = juniorToolResultSchema.extend({
|
|
8393
|
+
model_profile: profileSchema
|
|
8394
|
+
});
|
|
8395
|
+
const profileNames = handoff.profiles.map((profile) => `\`${profile}\``).join(", ");
|
|
8428
8396
|
return zodTool({
|
|
8429
|
-
description:
|
|
8397
|
+
description: `Permanently switch this conversation to a more capable model profile, replace prior context with a continuation summary, and continue the same task with the same workspace and all other normal tools. Available profiles: ${profileNames}. Omit profile to use \`${defaultProfile}\`. Call it as the only tool in the assistant message when the system tool policy requires a model upgrade.`,
|
|
8398
|
+
executionMode: "sequential",
|
|
8430
8399
|
inputSchema: z33.object({
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
"Curated evidence packet: relevant requirements, constraints, current plan, alternatives, code snippets, diffs, command output, and open questions."
|
|
8400
|
+
profile: profileSchema.nullish().describe(
|
|
8401
|
+
"Named model profile to use for the rest of the conversation; omit or pass null for the default"
|
|
8434
8402
|
)
|
|
8435
|
-
}),
|
|
8436
|
-
outputSchema:
|
|
8437
|
-
execute: async (
|
|
8438
|
-
|
|
8439
|
-
|
|
8440
|
-
|
|
8441
|
-
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
const advisorQuestion = question.trim();
|
|
8445
|
-
if (typeof suppliedContext !== "string" || !suppliedContext.trim()) {
|
|
8446
|
-
return failure(
|
|
8447
|
-
"invalid_context",
|
|
8448
|
-
"Advisor guidance is unavailable because the curated context was empty or invalid. Include the relevant evidence and constraints before retrying."
|
|
8449
|
-
);
|
|
8450
|
-
}
|
|
8451
|
-
const advisorContext = suppliedContext.trim();
|
|
8452
|
-
if (!context.conversationId) {
|
|
8453
|
-
return failure(
|
|
8454
|
-
"missing_conversation_id",
|
|
8455
|
-
"Advisor guidance is unavailable because this turn has no parent conversation id. Continue without assuming advisor history."
|
|
8456
|
-
);
|
|
8457
|
-
}
|
|
8458
|
-
const conversationId = context.conversationId;
|
|
8459
|
-
const childConversationId = advisorChildConversationId(conversationId);
|
|
8460
|
-
const subagentInvocationId = typeof toolCallId === "string" && toolCallId.length > 0 ? toolCallId : `advisor:${Date.now()}`;
|
|
8461
|
-
const endSubagent = async (outcome, errorCode) => {
|
|
8462
|
-
try {
|
|
8463
|
-
await getAgentStepStore().append(conversationId, [
|
|
8464
|
-
{
|
|
8465
|
-
entry: {
|
|
8466
|
-
type: "subagent_ended",
|
|
8467
|
-
subagentInvocationId,
|
|
8468
|
-
outcome,
|
|
8469
|
-
...errorCode ? { errorCode } : {}
|
|
8470
|
-
},
|
|
8471
|
-
createdAtMs: Date.now()
|
|
8472
|
-
}
|
|
8473
|
-
]);
|
|
8474
|
-
} catch (error) {
|
|
8475
|
-
logWarn("advisor_subagent_ended_append_failed", spanContext, {
|
|
8476
|
-
"exception.message": error instanceof Error ? error.message : String(error)
|
|
8477
|
-
});
|
|
8478
|
-
}
|
|
8479
|
-
};
|
|
8480
|
-
try {
|
|
8481
|
-
await conversationStore().ensureChildConversation({
|
|
8482
|
-
conversationId: childConversationId,
|
|
8483
|
-
parentConversationId: conversationId
|
|
8484
|
-
});
|
|
8485
|
-
await getAgentStepStore().append(conversationId, [
|
|
8486
|
-
{
|
|
8487
|
-
entry: {
|
|
8488
|
-
type: "subagent_started",
|
|
8489
|
-
subagentInvocationId,
|
|
8490
|
-
subagentKind: "advisor",
|
|
8491
|
-
modelId: context.config.modelId,
|
|
8492
|
-
...typeof toolCallId === "string" && toolCallId.length > 0 ? { parentToolCallId: toolCallId } : {},
|
|
8493
|
-
childConversationId,
|
|
8494
|
-
historyMode: "shared",
|
|
8495
|
-
reasoningLevel: context.config.thinkingLevel
|
|
8496
|
-
},
|
|
8497
|
-
createdAtMs: Date.now()
|
|
8498
|
-
}
|
|
8499
|
-
]);
|
|
8500
|
-
} catch (error) {
|
|
8501
|
-
logWarn("advisor_subagent_started_append_failed", spanContext, {
|
|
8502
|
-
"exception.message": error instanceof Error ? error.message : String(error)
|
|
8503
|
-
});
|
|
8504
|
-
}
|
|
8505
|
-
const conversationPrivacy = context.conversationPrivacy ?? "private";
|
|
8506
|
-
const requestText = renderAdvisorRequest(advisorQuestion, advisorContext);
|
|
8507
|
-
const advisorInputMessage = {
|
|
8508
|
-
role: "user",
|
|
8509
|
-
content: [{ type: "text", text: requestText }],
|
|
8510
|
-
timestamp: Date.now()
|
|
8403
|
+
}).strict(),
|
|
8404
|
+
outputSchema: handoffResultSchema,
|
|
8405
|
+
execute: async (input, options) => {
|
|
8406
|
+
const profile = input.profile ?? defaultProfile;
|
|
8407
|
+
await handoff.execute(profile, options.signal);
|
|
8408
|
+
return {
|
|
8409
|
+
ok: true,
|
|
8410
|
+
status: "success",
|
|
8411
|
+
model_profile: profile
|
|
8511
8412
|
};
|
|
8512
|
-
const advisorInputMessagesAttribute = serializeGenAiAttribute(
|
|
8513
|
-
conversationPrivacy !== "public" ? [toGenAiMessageMetadata(advisorInputMessage)] : [toCanonicalInputMessage(advisorInputMessage)]
|
|
8514
|
-
);
|
|
8515
|
-
return await withSpan(
|
|
8516
|
-
`invoke_agent ${context.config.modelId}`,
|
|
8517
|
-
"gen_ai.invoke_agent",
|
|
8518
|
-
spanContext,
|
|
8519
|
-
async () => {
|
|
8520
|
-
const requestMessage = {
|
|
8521
|
-
role: "user",
|
|
8522
|
-
content: [{ type: "text", text: requestText }],
|
|
8523
|
-
timestamp: Date.now()
|
|
8524
|
-
};
|
|
8525
|
-
let advisorMessages;
|
|
8526
|
-
try {
|
|
8527
|
-
advisorMessages = await loadProjection({
|
|
8528
|
-
conversationId: childConversationId
|
|
8529
|
-
});
|
|
8530
|
-
} catch {
|
|
8531
|
-
setSpanStatus("error");
|
|
8532
|
-
await endSubagent("error", "session_unavailable");
|
|
8533
|
-
return failure(
|
|
8534
|
-
"session_unavailable",
|
|
8535
|
-
"Advisor guidance is unavailable because advisor history could not be loaded. Continue without assuming advisor history."
|
|
8536
|
-
);
|
|
8537
|
-
}
|
|
8538
|
-
const apiKeyOverride = getPiGatewayApiKey();
|
|
8539
|
-
const advisorAgent = new Agent({
|
|
8540
|
-
...apiKeyOverride ? { getApiKey: () => apiKeyOverride } : {},
|
|
8541
|
-
initialState: {
|
|
8542
|
-
systemPrompt: ADVISOR_SYSTEM_PROMPT,
|
|
8543
|
-
model: resolveGatewayModel(context.config.modelId),
|
|
8544
|
-
thinkingLevel: context.config.thinkingLevel,
|
|
8545
|
-
tools: context.getTools()
|
|
8546
|
-
},
|
|
8547
|
-
sessionId: childConversationId,
|
|
8548
|
-
streamFn: context.streamFn
|
|
8549
|
-
});
|
|
8550
|
-
advisorAgent.state.messages = advisorMessages;
|
|
8551
|
-
const beforeMessageCount = advisorAgent.state.messages.length;
|
|
8552
|
-
try {
|
|
8553
|
-
await advisorAgent.prompt(requestMessage);
|
|
8554
|
-
} catch {
|
|
8555
|
-
setSpanStatus("error");
|
|
8556
|
-
await endSubagent("error", "unavailable");
|
|
8557
|
-
return failure(
|
|
8558
|
-
"unavailable",
|
|
8559
|
-
"Advisor guidance is unavailable. Continue without advisor guidance if the next step is clear from verified evidence."
|
|
8560
|
-
);
|
|
8561
|
-
}
|
|
8562
|
-
const assistant = lastAssistantMessage(advisorAgent.state.messages);
|
|
8563
|
-
const newAdvisorMessages = advisorAgent.state.messages.slice(beforeMessageCount);
|
|
8564
|
-
const outputMessages = newAdvisorMessages.filter(isAssistantMessage);
|
|
8565
|
-
const outputMessagesAttribute = serializeGenAiAttribute(
|
|
8566
|
-
conversationPrivacy !== "public" ? outputMessages.map(toGenAiMessageMetadata) : outputMessages.map(toCanonicalOutputMessage)
|
|
8567
|
-
);
|
|
8568
|
-
setSpanAttributes({
|
|
8569
|
-
...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
|
|
8570
|
-
...toGenAiMessagesTraceAttributes("app.ai.output", outputMessages),
|
|
8571
|
-
...extractGenAiUsageAttributes(...newAdvisorMessages)
|
|
8572
|
-
});
|
|
8573
|
-
if (!assistant || assistant.stopReason === "error" || assistant.stopReason === "aborted") {
|
|
8574
|
-
setSpanStatus("error");
|
|
8575
|
-
await endSubagent(
|
|
8576
|
-
assistant?.stopReason === "aborted" ? "aborted" : "error",
|
|
8577
|
-
"unavailable"
|
|
8578
|
-
);
|
|
8579
|
-
return failure(
|
|
8580
|
-
"unavailable",
|
|
8581
|
-
"Advisor guidance is unavailable. Continue without advisor guidance if the next step is clear from verified evidence."
|
|
8582
|
-
);
|
|
8583
|
-
}
|
|
8584
|
-
const memo = extractAssistantText(assistant);
|
|
8585
|
-
try {
|
|
8586
|
-
await commitMessages({
|
|
8587
|
-
conversationId: childConversationId,
|
|
8588
|
-
messages: advisorAgent.state.messages
|
|
8589
|
-
});
|
|
8590
|
-
} catch {
|
|
8591
|
-
setSpanStatus("error");
|
|
8592
|
-
await endSubagent("error", "session_unavailable");
|
|
8593
|
-
return failure(
|
|
8594
|
-
"session_unavailable",
|
|
8595
|
-
"Advisor guidance is unavailable because advisor history could not be saved. Retry the advisor call or continue without assuming advisor history."
|
|
8596
|
-
);
|
|
8597
|
-
}
|
|
8598
|
-
await endSubagent("success");
|
|
8599
|
-
setSpanStatus("ok");
|
|
8600
|
-
return success(memo);
|
|
8601
|
-
},
|
|
8602
|
-
{
|
|
8603
|
-
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
8604
|
-
"gen_ai.operation.name": "invoke_agent",
|
|
8605
|
-
"gen_ai.request.model": context.config.modelId,
|
|
8606
|
-
"gen_ai.output.type": "text",
|
|
8607
|
-
"server.address": GEN_AI_SERVER_ADDRESS,
|
|
8608
|
-
"server.port": GEN_AI_SERVER_PORT,
|
|
8609
|
-
"app.conversation.privacy": conversationPrivacy,
|
|
8610
|
-
...toGenAiMessagesTraceAttributes("app.ai.input", [
|
|
8611
|
-
advisorInputMessage
|
|
8612
|
-
]),
|
|
8613
|
-
...advisorInputMessagesAttribute ? { "gen_ai.input.messages": advisorInputMessagesAttribute } : {}
|
|
8614
|
-
}
|
|
8615
|
-
);
|
|
8616
8413
|
}
|
|
8617
8414
|
});
|
|
8618
8415
|
}
|
|
@@ -9340,8 +9137,8 @@ function createTools(availableSkills, hooks = {}, context) {
|
|
|
9340
9137
|
hooks.toolOverrides?.imageGenerate
|
|
9341
9138
|
);
|
|
9342
9139
|
}
|
|
9343
|
-
if (context.
|
|
9344
|
-
tools.
|
|
9140
|
+
if (context.handoff) {
|
|
9141
|
+
tools.handoff = createHandoffTool(context.handoff);
|
|
9345
9142
|
}
|
|
9346
9143
|
if (canUseResourceEventSubscriptionTools(context)) {
|
|
9347
9144
|
tools.subscribeToResourceEvents = createSubscribeToResourceEventsTool(context);
|
|
@@ -14539,7 +14336,6 @@ async function wireAgentTools(args) {
|
|
|
14539
14336
|
const loadableSkills = args.availableSkills.filter(
|
|
14540
14337
|
(skill) => skill.disableModelInvocation !== true || skill.name === args.invokedSkill?.name
|
|
14541
14338
|
);
|
|
14542
|
-
let advisorTools = [];
|
|
14543
14339
|
const commonToolRuntimeContext = {
|
|
14544
14340
|
conversationId: args.sessionConversationId,
|
|
14545
14341
|
userText: args.userInput,
|
|
@@ -14565,16 +14361,7 @@ async function wireAgentTools(args) {
|
|
|
14565
14361
|
mcpToolManager,
|
|
14566
14362
|
sandbox,
|
|
14567
14363
|
surface: args.surface,
|
|
14568
|
-
|
|
14569
|
-
config: botConfig.advisor,
|
|
14570
|
-
conversationId: args.sessionConversationId,
|
|
14571
|
-
conversationPrivacy: args.conversationPrivacy,
|
|
14572
|
-
logContext: args.spanContext,
|
|
14573
|
-
getTools: () => advisorTools,
|
|
14574
|
-
streamFn: createTracedStreamFn({
|
|
14575
|
-
conversationPrivacy: args.conversationPrivacy
|
|
14576
|
-
})
|
|
14577
|
-
}
|
|
14364
|
+
...args.requestHandoff ? { handoff: args.requestHandoff } : {}
|
|
14578
14365
|
};
|
|
14579
14366
|
const toolDestination = toolInvocationDestination(args.routing);
|
|
14580
14367
|
let toolRuntimeContext;
|
|
@@ -14720,18 +14507,6 @@ async function wireAgentTools(args) {
|
|
|
14720
14507
|
args.conversationPrivacy,
|
|
14721
14508
|
args.observers.onToolResult
|
|
14722
14509
|
);
|
|
14723
|
-
advisorTools = createPiAgentTools(
|
|
14724
|
-
createAdvisorToolDefinitions(tools),
|
|
14725
|
-
args.skillSandbox,
|
|
14726
|
-
args.spanContext,
|
|
14727
|
-
args.observers.onStatus,
|
|
14728
|
-
sandboxExecutor,
|
|
14729
|
-
pluginAuth,
|
|
14730
|
-
onToolCall,
|
|
14731
|
-
pluginHooks,
|
|
14732
|
-
args.conversationPrivacy,
|
|
14733
|
-
args.observers.onToolResult
|
|
14734
|
-
);
|
|
14735
14510
|
return {
|
|
14736
14511
|
activeMcpCatalogs,
|
|
14737
14512
|
agentTools,
|
|
@@ -14770,7 +14545,7 @@ function createResumeState(args) {
|
|
|
14770
14545
|
sessionId: args.sessionId,
|
|
14771
14546
|
loadedSkillNames: args.getLoadedSkillNames(),
|
|
14772
14547
|
logContext: args.logContext,
|
|
14773
|
-
modelId: args.
|
|
14548
|
+
modelId: args.getModelId(),
|
|
14774
14549
|
...args.getReasoningLevel() ? { reasoningLevel: args.getReasoningLevel() } : {},
|
|
14775
14550
|
actor: args.actor,
|
|
14776
14551
|
...args.surface ? { surface: args.surface } : {}
|
|
@@ -14794,6 +14569,11 @@ function createResumeState(args) {
|
|
|
14794
14569
|
setBeforeMessageCount(count) {
|
|
14795
14570
|
beforeMessageCount = count;
|
|
14796
14571
|
},
|
|
14572
|
+
/** Adopt an already committed epoch replacement as every resume baseline. */
|
|
14573
|
+
adoptCommittedBoundary(messages) {
|
|
14574
|
+
latestSafeBoundaryMessages = [...messages];
|
|
14575
|
+
resumeMessages = [...messages];
|
|
14576
|
+
},
|
|
14797
14577
|
captureResumeSnapshot(messages) {
|
|
14798
14578
|
resumeMessages = [...messages];
|
|
14799
14579
|
},
|
|
@@ -14938,6 +14718,319 @@ function createResumeState(args) {
|
|
|
14938
14718
|
};
|
|
14939
14719
|
}
|
|
14940
14720
|
|
|
14721
|
+
// src/chat/services/context-compaction.ts
|
|
14722
|
+
import {
|
|
14723
|
+
estimateContextTokens,
|
|
14724
|
+
estimateTokens
|
|
14725
|
+
} from "@earendil-works/pi-agent-core";
|
|
14726
|
+
var RETAINED_USER_MESSAGE_TOKENS = 2e4;
|
|
14727
|
+
var MAX_SUMMARY_INPUT_CHARS = 8e4;
|
|
14728
|
+
var MAX_VISIBLE_CONTEXT_CHARS = 2e4;
|
|
14729
|
+
var MAX_SUMMARY_CHARS = 6e3;
|
|
14730
|
+
var MAX_RENDERED_MESSAGE_CHARS = 4e3;
|
|
14731
|
+
var COMPACTION_SUMMARY_PREFIX = "Context compaction summary for future Junior turns:";
|
|
14732
|
+
var LEGACY_COMPACTION_SUMMARY_PREFIX = "Context handoff summary for future Junior turns:";
|
|
14733
|
+
var MODEL_HANDOFF_SUMMARY_PREFIX = "Model handoff checkpoint. Continue the outstanding request now using this summary as the complete prior context:";
|
|
14734
|
+
var OMITTED_OLDER_CONTEXT_NOTICE = "[older context omitted]";
|
|
14735
|
+
function textPart(value) {
|
|
14736
|
+
if (value && typeof value === "object" && value.type === "text" && typeof value.text === "string") {
|
|
14737
|
+
return value.text;
|
|
14738
|
+
}
|
|
14739
|
+
return void 0;
|
|
14740
|
+
}
|
|
14741
|
+
function messageText(message) {
|
|
14742
|
+
const content = message.content;
|
|
14743
|
+
const unwrapTask = message.role === "user";
|
|
14744
|
+
const displayText = (text) => unwrapTask ? unwrapCurrentInstruction(text) ?? text : text;
|
|
14745
|
+
if (!Array.isArray(content)) {
|
|
14746
|
+
return typeof content === "string" ? displayText(content) : "";
|
|
14747
|
+
}
|
|
14748
|
+
return content.map(textPart).filter((text) => Boolean(text)).map(displayText).join("\n").trim();
|
|
14749
|
+
}
|
|
14750
|
+
function sanitizeText(text) {
|
|
14751
|
+
return text.replace(
|
|
14752
|
+
/<data_base64>[\s\S]*?<\/data_base64>/g,
|
|
14753
|
+
"<data_base64>[omitted]</data_base64>"
|
|
14754
|
+
).replace(
|
|
14755
|
+
/data:image\/[a-z0-9.+-]+;base64,[a-z0-9+/=]+/gi,
|
|
14756
|
+
"[image data omitted]"
|
|
14757
|
+
).replaceAll("\0", " ").trim();
|
|
14758
|
+
}
|
|
14759
|
+
function truncateToTokenBudget(text, maxTokens) {
|
|
14760
|
+
const maxChars = Math.max(0, maxTokens * 4);
|
|
14761
|
+
if (text.length <= maxChars) {
|
|
14762
|
+
return text;
|
|
14763
|
+
}
|
|
14764
|
+
return `${text.slice(0, Math.max(0, maxChars - 3)).trimEnd()}...`;
|
|
14765
|
+
}
|
|
14766
|
+
function isCompactionSummary(text) {
|
|
14767
|
+
const normalized = text.trimStart();
|
|
14768
|
+
return normalized.startsWith(COMPACTION_SUMMARY_PREFIX) || normalized.startsWith(LEGACY_COMPACTION_SUMMARY_PREFIX) || normalized.startsWith(MODEL_HANDOFF_SUMMARY_PREFIX);
|
|
14769
|
+
}
|
|
14770
|
+
function isPayloadHeavy(text) {
|
|
14771
|
+
return /<data_base64>[\s\S]*?<\/data_base64>|data:image\/[a-z0-9.+-]+;base64,/i.test(
|
|
14772
|
+
text
|
|
14773
|
+
);
|
|
14774
|
+
}
|
|
14775
|
+
function userMessage(text) {
|
|
14776
|
+
return {
|
|
14777
|
+
role: "user",
|
|
14778
|
+
content: [{ type: "text", text }],
|
|
14779
|
+
timestamp: Date.now()
|
|
14780
|
+
};
|
|
14781
|
+
}
|
|
14782
|
+
function piMessageTimestamp(message) {
|
|
14783
|
+
const timestamp = message.timestamp;
|
|
14784
|
+
return typeof timestamp === "number" ? timestamp : Date.now();
|
|
14785
|
+
}
|
|
14786
|
+
function selectRetainedUserMessageEntries(messages, maxTokens = RETAINED_USER_MESSAGE_TOKENS) {
|
|
14787
|
+
const selected = [];
|
|
14788
|
+
let remaining = maxTokens;
|
|
14789
|
+
for (let sourceIndex = messages.length - 1; sourceIndex >= 0; sourceIndex -= 1) {
|
|
14790
|
+
const stripped = stripRuntimeTurnContext([messages[sourceIndex]]);
|
|
14791
|
+
const message = stripped[0];
|
|
14792
|
+
if (!message || message.role !== "user" || remaining <= 0) {
|
|
14793
|
+
continue;
|
|
14794
|
+
}
|
|
14795
|
+
const text = sanitizeText(messageText(message));
|
|
14796
|
+
if (!text || isCompactionSummary(text) || isPayloadHeavy(text)) {
|
|
14797
|
+
continue;
|
|
14798
|
+
}
|
|
14799
|
+
const tokens = estimateTextTokens(text);
|
|
14800
|
+
if (tokens <= remaining) {
|
|
14801
|
+
selected.push({ message: userMessage(text), sourceIndex });
|
|
14802
|
+
remaining -= tokens;
|
|
14803
|
+
continue;
|
|
14804
|
+
}
|
|
14805
|
+
const truncated = truncateToTokenBudget(text, remaining);
|
|
14806
|
+
if (truncated) {
|
|
14807
|
+
selected.push({ message: userMessage(truncated), sourceIndex });
|
|
14808
|
+
}
|
|
14809
|
+
break;
|
|
14810
|
+
}
|
|
14811
|
+
return selected.reverse();
|
|
14812
|
+
}
|
|
14813
|
+
function renderMessageForSummary(message) {
|
|
14814
|
+
const role = message.role;
|
|
14815
|
+
if (typeof role !== "string") {
|
|
14816
|
+
return void 0;
|
|
14817
|
+
}
|
|
14818
|
+
const text = sanitizeText(messageText(message));
|
|
14819
|
+
if (!text) {
|
|
14820
|
+
return void 0;
|
|
14821
|
+
}
|
|
14822
|
+
const trimmed = text.length > MAX_RENDERED_MESSAGE_CHARS ? `${text.slice(0, MAX_RENDERED_MESSAGE_CHARS).trimEnd()}...` : text;
|
|
14823
|
+
return `[${role}] ${trimmed}`;
|
|
14824
|
+
}
|
|
14825
|
+
function keepTail(text, maxChars) {
|
|
14826
|
+
if (text.length <= maxChars) {
|
|
14827
|
+
return text;
|
|
14828
|
+
}
|
|
14829
|
+
const prefix = `${OMITTED_OLDER_CONTEXT_NOTICE}
|
|
14830
|
+
`;
|
|
14831
|
+
return `${prefix}${text.slice(Math.max(0, text.length - maxChars + prefix.length))}`;
|
|
14832
|
+
}
|
|
14833
|
+
function renderSummaryInput(piMessages, conversationContext) {
|
|
14834
|
+
const lines = [];
|
|
14835
|
+
const visibleContext = conversationContext?.trim();
|
|
14836
|
+
if (visibleContext) {
|
|
14837
|
+
lines.push(
|
|
14838
|
+
"<visible-thread-context>",
|
|
14839
|
+
keepTail(visibleContext, MAX_VISIBLE_CONTEXT_CHARS),
|
|
14840
|
+
"</visible-thread-context>",
|
|
14841
|
+
""
|
|
14842
|
+
);
|
|
14843
|
+
}
|
|
14844
|
+
const renderedPiMessages = stripRuntimeTurnContext(piMessages).map(renderMessageForSummary).filter((line) => Boolean(line));
|
|
14845
|
+
if (renderedPiMessages.length > 0) {
|
|
14846
|
+
const piEnvelopeChars = "<pi-history>\n</pi-history>".length + 2;
|
|
14847
|
+
const piHistory = keepTail(
|
|
14848
|
+
renderedPiMessages.join("\n"),
|
|
14849
|
+
Math.max(
|
|
14850
|
+
1,
|
|
14851
|
+
MAX_SUMMARY_INPUT_CHARS - lines.join("\n").length - piEnvelopeChars
|
|
14852
|
+
)
|
|
14853
|
+
);
|
|
14854
|
+
lines.push("<pi-history>", piHistory, "</pi-history>");
|
|
14855
|
+
}
|
|
14856
|
+
return keepTail(lines.join("\n"), MAX_SUMMARY_INPUT_CHARS);
|
|
14857
|
+
}
|
|
14858
|
+
async function summarizeContext(args, deps) {
|
|
14859
|
+
const source = renderSummaryInput(args.piMessages, args.conversationContext);
|
|
14860
|
+
const result = await deps.completeText({
|
|
14861
|
+
modelId: botConfig.fastModelId,
|
|
14862
|
+
messageAttributeMode: "metadata",
|
|
14863
|
+
temperature: 0,
|
|
14864
|
+
signal: args.signal,
|
|
14865
|
+
messages: [
|
|
14866
|
+
{
|
|
14867
|
+
role: "user",
|
|
14868
|
+
content: [
|
|
14869
|
+
"You are performing a CONTEXT CHECKPOINT COMPACTION for Junior.",
|
|
14870
|
+
"Create a concise continuation summary for the agent that will continue this Slack thread.",
|
|
14871
|
+
"",
|
|
14872
|
+
"Include:",
|
|
14873
|
+
"- Current outstanding asks",
|
|
14874
|
+
"- Key decisions, completed work, and outcomes",
|
|
14875
|
+
"- Durable constraints, user preferences, IDs, URLs, artifacts, canvas links, sandbox references, and auth state",
|
|
14876
|
+
"- Clear next steps and unresolved blockers",
|
|
14877
|
+
"",
|
|
14878
|
+
"Do not invent details. Do not include raw secrets or credentials.",
|
|
14879
|
+
"",
|
|
14880
|
+
source
|
|
14881
|
+
].join("\n"),
|
|
14882
|
+
timestamp: Date.now()
|
|
14883
|
+
}
|
|
14884
|
+
],
|
|
14885
|
+
metadata: {
|
|
14886
|
+
modelId: botConfig.fastModelId,
|
|
14887
|
+
threadId: args.metadata?.threadId ?? "",
|
|
14888
|
+
channelId: args.metadata?.channelId ?? "",
|
|
14889
|
+
actorId: args.metadata?.actorId ?? "",
|
|
14890
|
+
runId: args.metadata?.runId ?? ""
|
|
14891
|
+
}
|
|
14892
|
+
});
|
|
14893
|
+
const summary = result.text.trim();
|
|
14894
|
+
if (!summary) {
|
|
14895
|
+
throw new Error("Compaction summary was empty");
|
|
14896
|
+
}
|
|
14897
|
+
return summary.slice(0, MAX_SUMMARY_CHARS);
|
|
14898
|
+
}
|
|
14899
|
+
function estimateHistoryTokens(messages) {
|
|
14900
|
+
const stripped = stripRuntimeTurnContext(messages);
|
|
14901
|
+
const usageEstimate = estimateContextTokens(stripped).tokens;
|
|
14902
|
+
const structuralEstimate = stripped.reduce(
|
|
14903
|
+
(total, message) => total + estimateTokens(message),
|
|
14904
|
+
0
|
|
14905
|
+
);
|
|
14906
|
+
return Math.max(usageEstimate, structuralEstimate);
|
|
14907
|
+
}
|
|
14908
|
+
function buildReplacementProvenance(args) {
|
|
14909
|
+
return [
|
|
14910
|
+
...args.retained.map(
|
|
14911
|
+
(entry) => args.sourceProvenance[entry.sourceIndex] ?? contextProvenance
|
|
14912
|
+
),
|
|
14913
|
+
contextProvenance
|
|
14914
|
+
];
|
|
14915
|
+
}
|
|
14916
|
+
function loadCompactionSource(messages) {
|
|
14917
|
+
if (messages.length > 0) {
|
|
14918
|
+
return {
|
|
14919
|
+
estimatedTokens: estimateHistoryTokens(messages),
|
|
14920
|
+
messages
|
|
14921
|
+
};
|
|
14922
|
+
}
|
|
14923
|
+
return { reason: "missing_context" };
|
|
14924
|
+
}
|
|
14925
|
+
async function maybeCompactWithDeps(args, deps) {
|
|
14926
|
+
const source = loadCompactionSource(args.piMessages);
|
|
14927
|
+
if ("reason" in source) {
|
|
14928
|
+
return { compacted: false, reason: source.reason };
|
|
14929
|
+
}
|
|
14930
|
+
const triggerTokens = deps.autoCompactionTriggerTokens ?? getAgentContextCompactionTriggerTokens();
|
|
14931
|
+
if (source.estimatedTokens <= triggerTokens) {
|
|
14932
|
+
return { compacted: false, reason: "below_threshold" };
|
|
14933
|
+
}
|
|
14934
|
+
args.onCompactionStart?.();
|
|
14935
|
+
let summary;
|
|
14936
|
+
try {
|
|
14937
|
+
summary = await summarizeContext(
|
|
14938
|
+
{
|
|
14939
|
+
conversationContext: args.conversationContext,
|
|
14940
|
+
piMessages: source.messages,
|
|
14941
|
+
metadata: args.metadata
|
|
14942
|
+
},
|
|
14943
|
+
deps
|
|
14944
|
+
);
|
|
14945
|
+
} catch (error) {
|
|
14946
|
+
logWarn(
|
|
14947
|
+
"context_compaction_summary_failed",
|
|
14948
|
+
{
|
|
14949
|
+
slackThreadId: args.metadata?.threadId,
|
|
14950
|
+
slackUserId: args.metadata?.actorId,
|
|
14951
|
+
slackChannelId: args.metadata?.channelId,
|
|
14952
|
+
runId: args.metadata?.runId,
|
|
14953
|
+
assistantUserName: botConfig.userName,
|
|
14954
|
+
modelId: botConfig.fastModelId
|
|
14955
|
+
},
|
|
14956
|
+
{
|
|
14957
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
14958
|
+
},
|
|
14959
|
+
"Context compaction failed; continuing with prior history"
|
|
14960
|
+
);
|
|
14961
|
+
return { compacted: false, reason: "summary_failed" };
|
|
14962
|
+
}
|
|
14963
|
+
return await writeCompactedThreadContext(args, source.messages, summary, {
|
|
14964
|
+
estimatedTokens: source.estimatedTokens,
|
|
14965
|
+
triggerTokens
|
|
14966
|
+
});
|
|
14967
|
+
}
|
|
14968
|
+
async function writeCompactedThreadContext(args, sourceMessages, summary, context) {
|
|
14969
|
+
const stepStore = getAgentStepStore();
|
|
14970
|
+
const sourceProjection = await loadConversationProjection({
|
|
14971
|
+
conversationId: args.conversationId
|
|
14972
|
+
});
|
|
14973
|
+
const retained = selectRetainedUserMessageEntries(
|
|
14974
|
+
trimTrailingAssistantMessages(sourceProjection.messages)
|
|
14975
|
+
);
|
|
14976
|
+
const replacement = [
|
|
14977
|
+
...retained.map((entry) => entry.message),
|
|
14978
|
+
userMessage(`${COMPACTION_SUMMARY_PREFIX}
|
|
14979
|
+
${summary}`)
|
|
14980
|
+
];
|
|
14981
|
+
const replacementProvenance = buildReplacementProvenance({
|
|
14982
|
+
retained,
|
|
14983
|
+
sourceProvenance: sourceProjection.provenance
|
|
14984
|
+
});
|
|
14985
|
+
await stepStore.startEpoch(args.conversationId, {
|
|
14986
|
+
reason: "compaction",
|
|
14987
|
+
modelProfile: sourceProjection.modelProfile,
|
|
14988
|
+
modelId: modelIdForProfile(botConfig, sourceProjection.modelProfile),
|
|
14989
|
+
messages: replacement.map((message, index) => ({
|
|
14990
|
+
message,
|
|
14991
|
+
createdAtMs: piMessageTimestamp(message),
|
|
14992
|
+
provenance: replacementProvenance[index]
|
|
14993
|
+
}))
|
|
14994
|
+
});
|
|
14995
|
+
updateConversationStats(args.conversation);
|
|
14996
|
+
setSpanAttributes({
|
|
14997
|
+
"app.compaction.input_messages": sourceMessages.length,
|
|
14998
|
+
"app.compaction.retained_messages": replacement.length - 1,
|
|
14999
|
+
"app.compaction.summary_chars": summary.length,
|
|
15000
|
+
...context.triggerTokens !== void 0 ? { "app.compaction.trigger_tokens": context.triggerTokens } : {},
|
|
15001
|
+
"app.context_tokens_estimated": context.estimatedTokens
|
|
15002
|
+
});
|
|
15003
|
+
return {
|
|
15004
|
+
compacted: true,
|
|
15005
|
+
piMessages: replacement
|
|
15006
|
+
};
|
|
15007
|
+
}
|
|
15008
|
+
function createContextCompactor(deps) {
|
|
15009
|
+
return {
|
|
15010
|
+
maybeCompact: async (args) => await maybeCompactWithDeps(args, deps)
|
|
15011
|
+
};
|
|
15012
|
+
}
|
|
15013
|
+
async function compactContextForHandoff(args, deps) {
|
|
15014
|
+
const summary = await summarizeContext(args, deps);
|
|
15015
|
+
const message = userMessage(`${MODEL_HANDOFF_SUMMARY_PREFIX}
|
|
15016
|
+
${summary}`);
|
|
15017
|
+
const messages = [message];
|
|
15018
|
+
args.signal?.throwIfAborted();
|
|
15019
|
+
await getAgentStepStore().startEpoch(args.conversationId, {
|
|
15020
|
+
reason: "handoff",
|
|
15021
|
+
modelProfile: args.target.modelProfile,
|
|
15022
|
+
modelId: args.target.modelId,
|
|
15023
|
+
messages: [
|
|
15024
|
+
{
|
|
15025
|
+
message,
|
|
15026
|
+
createdAtMs: piMessageTimestamp(message),
|
|
15027
|
+
provenance: contextProvenance
|
|
15028
|
+
}
|
|
15029
|
+
]
|
|
15030
|
+
});
|
|
15031
|
+
return messages;
|
|
15032
|
+
}
|
|
15033
|
+
|
|
14941
15034
|
// src/chat/agent/index.ts
|
|
14942
15035
|
var AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
|
|
14943
15036
|
function waitForAbortSettlement(promise, timeoutMs) {
|
|
@@ -15006,7 +15099,10 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15006
15099
|
let connectedMcpProviders = /* @__PURE__ */ new Set();
|
|
15007
15100
|
let canRecordMcpProviders = false;
|
|
15008
15101
|
let turnUsage;
|
|
15102
|
+
let handoffPhaseUsage;
|
|
15009
15103
|
let thinkingSelection;
|
|
15104
|
+
let activeModelProfile = STANDARD_MODEL_PROFILE;
|
|
15105
|
+
let activeModelId = modelIdForProfile(botConfig, activeModelProfile);
|
|
15010
15106
|
const actor = actorFromRouting(routing);
|
|
15011
15107
|
const surface = surfaceFromRouting(routing);
|
|
15012
15108
|
const runSource = routing.source;
|
|
@@ -15022,8 +15118,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15022
15118
|
channelId: routing.correlation?.channelId,
|
|
15023
15119
|
runId: routing.correlation?.runId,
|
|
15024
15120
|
...credentialActorLogContext,
|
|
15025
|
-
assistantUserName: botConfig.userName
|
|
15026
|
-
modelId: botConfig.modelId
|
|
15121
|
+
assistantUserName: botConfig.userName
|
|
15027
15122
|
};
|
|
15028
15123
|
const { conversationId: sessionConversationId, sessionId } = getSessionIdentifiers(routing);
|
|
15029
15124
|
const recordConnectedMcpProvider = async (provider) => {
|
|
@@ -15049,6 +15144,14 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15049
15144
|
sandboxDependencyProfileHash: lastKnownSandboxDependencyProfileHash
|
|
15050
15145
|
});
|
|
15051
15146
|
try {
|
|
15147
|
+
if (sessionConversationId) {
|
|
15148
|
+
const projection = await openConversationProjection({
|
|
15149
|
+
conversationId: sessionConversationId,
|
|
15150
|
+
modelId: activeModelId
|
|
15151
|
+
});
|
|
15152
|
+
activeModelProfile = projection.modelProfile;
|
|
15153
|
+
activeModelId = modelIdForProfile(botConfig, activeModelProfile);
|
|
15154
|
+
}
|
|
15052
15155
|
const shouldTrace = shouldEmitDevAgentTrace();
|
|
15053
15156
|
const spanContext = {
|
|
15054
15157
|
conversationId: sessionConversationId,
|
|
@@ -15058,7 +15161,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15058
15161
|
runId: routing.correlation?.runId,
|
|
15059
15162
|
...credentialActorLogContext,
|
|
15060
15163
|
assistantUserName: botConfig.userName,
|
|
15061
|
-
modelId:
|
|
15164
|
+
modelId: activeModelId
|
|
15062
15165
|
};
|
|
15063
15166
|
const availableSkills = await discoverRunSkills({
|
|
15064
15167
|
skillDirs: policy.skillDirs,
|
|
@@ -15097,10 +15200,11 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15097
15200
|
currentSliceId,
|
|
15098
15201
|
existingSessionRecord
|
|
15099
15202
|
} = await restoreSessionRecord(routing);
|
|
15100
|
-
const committedInstructionProvenance =
|
|
15101
|
-
...existingSessionRecord
|
|
15203
|
+
const committedInstructionProvenance = [
|
|
15204
|
+
...existingSessionRecord?.piMessageProvenance ?? [],
|
|
15205
|
+
...(existingSessionRecord?.actors ?? []).map(instructionProvenanceFor),
|
|
15102
15206
|
...resumedFromSessionRecord ? [] : [instructionProvenanceFor(actor)]
|
|
15103
|
-
]
|
|
15207
|
+
];
|
|
15104
15208
|
const runActors = () => instructionActors(committedInstructionProvenance);
|
|
15105
15209
|
canRecordMcpProviders = Boolean(
|
|
15106
15210
|
sessionRecordState.canUseTurnSession && sessionConversationId && sessionId
|
|
@@ -15112,7 +15216,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15112
15216
|
getLoadedSkillNames: () => loadedSkillNamesForResume,
|
|
15113
15217
|
getReasoningLevel: () => thinkingSelection?.thinkingLevel,
|
|
15114
15218
|
logContext: sessionRecordLogContext,
|
|
15115
|
-
|
|
15219
|
+
getModelId: () => activeModelId,
|
|
15116
15220
|
recordActiveMcpProviders,
|
|
15117
15221
|
actor,
|
|
15118
15222
|
runSource,
|
|
@@ -15181,7 +15285,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15181
15285
|
messageText: userInput
|
|
15182
15286
|
});
|
|
15183
15287
|
setSpanAttributes({
|
|
15184
|
-
"gen_ai.request.model":
|
|
15288
|
+
"gen_ai.request.model": activeModelId,
|
|
15185
15289
|
"gen_ai.request.reasoning.level": thinkingSelection.thinkingLevel,
|
|
15186
15290
|
"app.ai.thinking_level_reason": thinkingSelection.reason,
|
|
15187
15291
|
...thinkingSelection.confidence !== void 0 ? {
|
|
@@ -15192,7 +15296,55 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15192
15296
|
const artifactStatePatch = {};
|
|
15193
15297
|
const toolCalls = [];
|
|
15194
15298
|
let agent;
|
|
15299
|
+
let pendingHandoff;
|
|
15195
15300
|
const currentAgentMessages = () => agent ? [...agent.state.messages] : [];
|
|
15301
|
+
const handoffProfiles = [
|
|
15302
|
+
DEFAULT_HANDOFF_MODEL_PROFILE,
|
|
15303
|
+
...Object.keys(botConfig.modelProfiles).filter((profile) => profile !== DEFAULT_HANDOFF_MODEL_PROFILE).sort()
|
|
15304
|
+
];
|
|
15305
|
+
const requestHandoff = activeModelProfile === STANDARD_MODEL_PROFILE && sessionConversationId ? {
|
|
15306
|
+
profiles: handoffProfiles,
|
|
15307
|
+
execute: async (profile, signal2) => {
|
|
15308
|
+
const sourceMessages = [...agent.state.messages];
|
|
15309
|
+
const runtimeContext = retainRuntimeTurnContext(sourceMessages);
|
|
15310
|
+
const standardPhaseUsage = extractGenAiUsageSummary(
|
|
15311
|
+
...sourceMessages.slice(runResume.beforeMessageCount).filter(isAssistantMessage)
|
|
15312
|
+
);
|
|
15313
|
+
const phaseUsage = hasAgentTurnUsage(standardPhaseUsage) ? standardPhaseUsage : void 0;
|
|
15314
|
+
const target = {
|
|
15315
|
+
modelId: modelIdForProfile(botConfig, profile),
|
|
15316
|
+
modelProfile: profile
|
|
15317
|
+
};
|
|
15318
|
+
const handoffModel = resolveGatewayModel(target.modelId);
|
|
15319
|
+
const handoffThinkingLevel = toAgentThinkingLevel(
|
|
15320
|
+
thinkingSelection.thinkingLevel
|
|
15321
|
+
);
|
|
15322
|
+
const handoffMessages = await compactContextForHandoff(
|
|
15323
|
+
{
|
|
15324
|
+
conversationContext: input.conversationContext,
|
|
15325
|
+
conversationId: sessionConversationId,
|
|
15326
|
+
piMessages: sourceMessages,
|
|
15327
|
+
signal: signal2,
|
|
15328
|
+
target,
|
|
15329
|
+
metadata: {
|
|
15330
|
+
threadId: routing.correlation?.threadId,
|
|
15331
|
+
channelId: routing.correlation?.channelId,
|
|
15332
|
+
actorId: routing.correlation?.actorId,
|
|
15333
|
+
runId: routing.correlation?.runId
|
|
15334
|
+
}
|
|
15335
|
+
},
|
|
15336
|
+
{ completeText }
|
|
15337
|
+
);
|
|
15338
|
+
handoffPhaseUsage = phaseUsage;
|
|
15339
|
+
pendingHandoff = {
|
|
15340
|
+
messages: [...handoffMessages, ...runtimeContext],
|
|
15341
|
+
model: handoffModel,
|
|
15342
|
+
thinkingLevel: handoffThinkingLevel
|
|
15343
|
+
};
|
|
15344
|
+
activeModelProfile = profile;
|
|
15345
|
+
activeModelId = target.modelId;
|
|
15346
|
+
}
|
|
15347
|
+
} : void 0;
|
|
15196
15348
|
setTags({
|
|
15197
15349
|
conversationId: spanContext.conversationId,
|
|
15198
15350
|
slackThreadId: routing.correlation?.threadId,
|
|
@@ -15201,7 +15353,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15201
15353
|
runId: routing.correlation?.runId,
|
|
15202
15354
|
...credentialActorLogContext,
|
|
15203
15355
|
assistantUserName: botConfig.userName,
|
|
15204
|
-
modelId:
|
|
15356
|
+
modelId: activeModelId
|
|
15205
15357
|
});
|
|
15206
15358
|
const wiring = await wireAgentTools({
|
|
15207
15359
|
abortAgent: () => agent?.abort(),
|
|
@@ -15225,6 +15377,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15225
15377
|
preAgentPromptMessages,
|
|
15226
15378
|
priorPiMessages,
|
|
15227
15379
|
recordConnectedMcpProvider,
|
|
15380
|
+
requestHandoff,
|
|
15228
15381
|
resume: runResume,
|
|
15229
15382
|
routing,
|
|
15230
15383
|
sessionConversationId,
|
|
@@ -15240,6 +15393,9 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15240
15393
|
mcpToolManager = wiring.mcpToolManager;
|
|
15241
15394
|
const sandboxExecutor = wiring.sandboxExecutor;
|
|
15242
15395
|
const getPendingAuthPause = wiring.getPendingAuthPause;
|
|
15396
|
+
const toolsAfterHandoff = wiring.agentTools.filter(
|
|
15397
|
+
(tool) => tool.name !== HANDOFF_TOOL_NAME
|
|
15398
|
+
);
|
|
15243
15399
|
const {
|
|
15244
15400
|
baseInstructions,
|
|
15245
15401
|
inputMessages,
|
|
@@ -15267,6 +15423,38 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15267
15423
|
});
|
|
15268
15424
|
let hasEmittedText = false;
|
|
15269
15425
|
let needsSeparator = false;
|
|
15426
|
+
let bufferedStandardText = "";
|
|
15427
|
+
let bufferedStandardMessageStart = false;
|
|
15428
|
+
const startAssistantMessage = () => {
|
|
15429
|
+
Promise.resolve(observers.onAssistantMessageStart?.()).catch((error) => {
|
|
15430
|
+
logWarn(
|
|
15431
|
+
"streaming_message_start_error",
|
|
15432
|
+
{},
|
|
15433
|
+
{
|
|
15434
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
15435
|
+
},
|
|
15436
|
+
"Failed to deliver assistant message start to stream coordinator"
|
|
15437
|
+
);
|
|
15438
|
+
});
|
|
15439
|
+
if (hasEmittedText) {
|
|
15440
|
+
needsSeparator = true;
|
|
15441
|
+
}
|
|
15442
|
+
};
|
|
15443
|
+
const deliverText = (deltaText) => {
|
|
15444
|
+
const text = needsSeparator ? "\n\n" + deltaText : deltaText;
|
|
15445
|
+
needsSeparator = false;
|
|
15446
|
+
hasEmittedText = true;
|
|
15447
|
+
Promise.resolve(observers.onTextDelta?.(text)).catch((error) => {
|
|
15448
|
+
logWarn(
|
|
15449
|
+
"streaming_text_delta_error",
|
|
15450
|
+
{},
|
|
15451
|
+
{
|
|
15452
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
15453
|
+
},
|
|
15454
|
+
"Failed to deliver text delta to stream"
|
|
15455
|
+
);
|
|
15456
|
+
});
|
|
15457
|
+
};
|
|
15270
15458
|
const drainSteeringMessages = async () => {
|
|
15271
15459
|
if (!durability.drainSteeringMessages || !sessionRecordState.canUseTurnSession || !sessionConversationId || !sessionId) {
|
|
15272
15460
|
return;
|
|
@@ -15315,18 +15503,60 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15315
15503
|
}
|
|
15316
15504
|
};
|
|
15317
15505
|
const apiKeyOverride = getPiGatewayApiKey();
|
|
15318
|
-
agent = new
|
|
15506
|
+
agent = new Agent({
|
|
15319
15507
|
...apiKeyOverride ? { getApiKey: () => apiKeyOverride } : {},
|
|
15320
15508
|
streamFn: createTracedStreamFn({ conversationPrivacy }),
|
|
15321
15509
|
steeringMode: "all",
|
|
15510
|
+
beforeToolCall: async ({ assistantMessage }) => {
|
|
15511
|
+
const toolCalls2 = assistantMessage.content.filter(
|
|
15512
|
+
(part) => part.type === "toolCall"
|
|
15513
|
+
);
|
|
15514
|
+
const containsHandoff = toolCalls2.some(
|
|
15515
|
+
(call) => call.name === HANDOFF_TOOL_NAME
|
|
15516
|
+
);
|
|
15517
|
+
if (containsHandoff && toolCalls2.length !== 1) {
|
|
15518
|
+
return {
|
|
15519
|
+
block: true,
|
|
15520
|
+
reason: "handoff must be the only tool call in its assistant message; reissue it alone"
|
|
15521
|
+
};
|
|
15522
|
+
}
|
|
15523
|
+
return void 0;
|
|
15524
|
+
},
|
|
15322
15525
|
prepareNextTurn: async () => {
|
|
15526
|
+
let update;
|
|
15527
|
+
if (pendingHandoff) {
|
|
15528
|
+
const { messages, model, thinkingLevel } = pendingHandoff;
|
|
15529
|
+
const replacement = [...messages];
|
|
15530
|
+
pendingHandoff = void 0;
|
|
15531
|
+
agent.state.messages = replacement;
|
|
15532
|
+
agent.state.model = model;
|
|
15533
|
+
agent.state.thinkingLevel = thinkingLevel;
|
|
15534
|
+
agent.state.tools = toolsAfterHandoff;
|
|
15535
|
+
runResume.setBeforeMessageCount(replacement.length);
|
|
15536
|
+
runResume.setTurnStartMessageIndex(0);
|
|
15537
|
+
runResume.adoptCommittedBoundary(replacement);
|
|
15538
|
+
setSpanAttributes({
|
|
15539
|
+
"gen_ai.request.model": activeModelId,
|
|
15540
|
+
"gen_ai.request.reasoning.level": thinkingSelection.thinkingLevel,
|
|
15541
|
+
"app.ai.model_profile": activeModelProfile
|
|
15542
|
+
});
|
|
15543
|
+
update = {
|
|
15544
|
+
context: {
|
|
15545
|
+
systemPrompt: baseInstructions,
|
|
15546
|
+
messages: replacement,
|
|
15547
|
+
tools: toolsAfterHandoff
|
|
15548
|
+
},
|
|
15549
|
+
model,
|
|
15550
|
+
thinkingLevel
|
|
15551
|
+
};
|
|
15552
|
+
}
|
|
15323
15553
|
await drainSteeringMessages();
|
|
15324
15554
|
runResume.yieldAtSafeBoundaryIfDue(currentAgentMessages());
|
|
15325
|
-
return
|
|
15555
|
+
return update;
|
|
15326
15556
|
},
|
|
15327
15557
|
initialState: {
|
|
15328
15558
|
systemPrompt: baseInstructions,
|
|
15329
|
-
model: resolveGatewayModel(
|
|
15559
|
+
model: resolveGatewayModel(activeModelId),
|
|
15330
15560
|
thinkingLevel: toAgentThinkingLevel(thinkingSelection.thinkingLevel),
|
|
15331
15561
|
tools: wiring.agentTools
|
|
15332
15562
|
}
|
|
@@ -15336,43 +15566,46 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15336
15566
|
return recordParentToolExecutionStart(event);
|
|
15337
15567
|
}
|
|
15338
15568
|
if (event.type === "turn_end" && event.toolResults.length > 0) {
|
|
15569
|
+
if (pendingHandoff) {
|
|
15570
|
+
return;
|
|
15571
|
+
}
|
|
15339
15572
|
return runResume.persistSafeBoundary([...agent.state.messages]).then(() => void 0);
|
|
15340
15573
|
}
|
|
15341
15574
|
if (event.type === "message_start") {
|
|
15342
|
-
|
|
15343
|
-
|
|
15344
|
-
|
|
15345
|
-
|
|
15346
|
-
|
|
15347
|
-
|
|
15348
|
-
|
|
15349
|
-
|
|
15350
|
-
|
|
15351
|
-
|
|
15352
|
-
|
|
15575
|
+
if (activeModelProfile === STANDARD_MODEL_PROFILE && requestHandoff) {
|
|
15576
|
+
bufferedStandardMessageStart = true;
|
|
15577
|
+
bufferedStandardText = "";
|
|
15578
|
+
} else {
|
|
15579
|
+
startAssistantMessage();
|
|
15580
|
+
}
|
|
15581
|
+
return;
|
|
15582
|
+
}
|
|
15583
|
+
if (event.type === "message_end" && isAssistantMessage(event.message)) {
|
|
15584
|
+
if (!bufferedStandardMessageStart) {
|
|
15585
|
+
return;
|
|
15586
|
+
}
|
|
15587
|
+
const containsHandoff = event.message.content.some(
|
|
15588
|
+
(part) => part.type === "toolCall" && part.name === HANDOFF_TOOL_NAME
|
|
15353
15589
|
);
|
|
15354
|
-
if (
|
|
15355
|
-
|
|
15590
|
+
if (!containsHandoff) {
|
|
15591
|
+
startAssistantMessage();
|
|
15592
|
+
if (bufferedStandardText) {
|
|
15593
|
+
deliverText(bufferedStandardText);
|
|
15594
|
+
}
|
|
15356
15595
|
}
|
|
15596
|
+
bufferedStandardMessageStart = false;
|
|
15597
|
+
bufferedStandardText = "";
|
|
15357
15598
|
return;
|
|
15358
15599
|
}
|
|
15359
15600
|
if (event.type !== "message_update") return;
|
|
15360
15601
|
if (event.assistantMessageEvent.type !== "text_delta") return;
|
|
15361
15602
|
const deltaText = event.assistantMessageEvent.delta;
|
|
15362
15603
|
if (!deltaText) return;
|
|
15363
|
-
|
|
15364
|
-
|
|
15365
|
-
|
|
15366
|
-
|
|
15367
|
-
|
|
15368
|
-
"streaming_text_delta_error",
|
|
15369
|
-
{},
|
|
15370
|
-
{
|
|
15371
|
-
"exception.message": error instanceof Error ? error.message : String(error)
|
|
15372
|
-
},
|
|
15373
|
-
"Failed to deliver text delta to stream"
|
|
15374
|
-
);
|
|
15375
|
-
});
|
|
15604
|
+
if (bufferedStandardMessageStart) {
|
|
15605
|
+
bufferedStandardText += deltaText;
|
|
15606
|
+
} else {
|
|
15607
|
+
deliverText(deltaText);
|
|
15608
|
+
}
|
|
15376
15609
|
});
|
|
15377
15610
|
let newMessages = [];
|
|
15378
15611
|
try {
|
|
@@ -15389,7 +15622,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15389
15622
|
runResume.setTurnStartMessageIndex(agent.state.messages.length);
|
|
15390
15623
|
}
|
|
15391
15624
|
await withSpan(
|
|
15392
|
-
`invoke_agent ${
|
|
15625
|
+
`invoke_agent ${activeModelId}`,
|
|
15393
15626
|
"gen_ai.invoke_agent",
|
|
15394
15627
|
spanContext,
|
|
15395
15628
|
async () => {
|
|
@@ -15454,7 +15687,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15454
15687
|
{
|
|
15455
15688
|
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
15456
15689
|
"gen_ai.operation.name": "invoke_agent",
|
|
15457
|
-
"gen_ai.request.model":
|
|
15690
|
+
"gen_ai.request.model": activeModelId,
|
|
15458
15691
|
...thinkingSelection ? {
|
|
15459
15692
|
"gen_ai.request.reasoning.level": thinkingSelection.thinkingLevel
|
|
15460
15693
|
} : {},
|
|
@@ -15519,7 +15752,11 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15519
15752
|
...outputMessages
|
|
15520
15753
|
);
|
|
15521
15754
|
const currentUsage = hasAgentTurnUsage(usageSummary) ? usageSummary : void 0;
|
|
15522
|
-
|
|
15755
|
+
const currentPhaseUsage = addAgentTurnUsage(
|
|
15756
|
+
retryUsage,
|
|
15757
|
+
currentUsage
|
|
15758
|
+
);
|
|
15759
|
+
turnUsage = addAgentTurnUsage(handoffPhaseUsage, currentPhaseUsage);
|
|
15523
15760
|
setSpanAttributes({
|
|
15524
15761
|
...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
|
|
15525
15762
|
...toGenAiMessagesTraceAttributes(
|
|
@@ -15543,7 +15780,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15543
15780
|
if (!providerRetry) {
|
|
15544
15781
|
break;
|
|
15545
15782
|
}
|
|
15546
|
-
retryUsage =
|
|
15783
|
+
retryUsage = currentPhaseUsage;
|
|
15547
15784
|
agent.state.messages = providerRetry.messages;
|
|
15548
15785
|
await runResume.persistSafeBoundary(providerRetry.messages);
|
|
15549
15786
|
logWarn(
|
|
@@ -15560,7 +15797,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15560
15797
|
{
|
|
15561
15798
|
"gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
|
|
15562
15799
|
"gen_ai.operation.name": "invoke_agent",
|
|
15563
|
-
"gen_ai.request.model":
|
|
15800
|
+
"gen_ai.request.model": activeModelId,
|
|
15564
15801
|
"gen_ai.output.type": "text",
|
|
15565
15802
|
"server.address": GEN_AI_SERVER_ADDRESS,
|
|
15566
15803
|
"server.port": GEN_AI_SERVER_PORT,
|
|
@@ -15596,7 +15833,8 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15596
15833
|
usage: turnUsage,
|
|
15597
15834
|
thinkingSelection,
|
|
15598
15835
|
correlation: routing.correlation,
|
|
15599
|
-
assistantUserName: botConfig.userName
|
|
15836
|
+
assistantUserName: botConfig.userName,
|
|
15837
|
+
modelId: activeModelId
|
|
15600
15838
|
})
|
|
15601
15839
|
};
|
|
15602
15840
|
} catch (error) {
|
|
@@ -15609,6 +15847,9 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15609
15847
|
return outcome;
|
|
15610
15848
|
}
|
|
15611
15849
|
}
|
|
15850
|
+
if (error instanceof ModelProfileNotConfiguredError) {
|
|
15851
|
+
throw error;
|
|
15852
|
+
}
|
|
15612
15853
|
if (isProviderRetryError(error)) {
|
|
15613
15854
|
throw error;
|
|
15614
15855
|
}
|
|
@@ -15631,7 +15872,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15631
15872
|
runId: routing.correlation?.runId,
|
|
15632
15873
|
...credentialActorLogContext,
|
|
15633
15874
|
assistantUserName: botConfig.userName,
|
|
15634
|
-
modelId:
|
|
15875
|
+
modelId: activeModelId
|
|
15635
15876
|
},
|
|
15636
15877
|
{},
|
|
15637
15878
|
"executeAgentRun failed"
|
|
@@ -15644,7 +15885,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy) {
|
|
|
15644
15885
|
...getSandboxMetadata(),
|
|
15645
15886
|
diagnostics: {
|
|
15646
15887
|
outcome: "provider_error",
|
|
15647
|
-
modelId:
|
|
15888
|
+
modelId: activeModelId,
|
|
15648
15889
|
assistantMessageCount: 0,
|
|
15649
15890
|
...thinkingSelection ? {
|
|
15650
15891
|
thinkingLevel: thinkingSelection.thinkingLevel
|
|
@@ -15731,6 +15972,7 @@ export {
|
|
|
15731
15972
|
finalizeMcpAuthorization,
|
|
15732
15973
|
resolvePluginOAuthAccount,
|
|
15733
15974
|
executeCredentialedEgressRequest,
|
|
15975
|
+
createContextCompactor,
|
|
15734
15976
|
executeAgentRun,
|
|
15735
15977
|
createAgentRunner
|
|
15736
15978
|
};
|