@m6d/cortex-server 1.6.0 → 1.8.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/index.d.ts +1 -1
- package/dist/src/{adapters → lib/adapters}/database.d.ts +16 -1
- package/dist/src/lib/ai/context/compressor.d.ts +18 -0
- package/dist/src/{ai → lib/ai}/context/index.d.ts +2 -1
- package/dist/src/lib/ai/context/intra-turn-compressor.d.ts +24 -0
- package/dist/src/{ai → lib/ai}/context/types.d.ts +2 -0
- package/dist/src/lib/config.d.ts +153 -0
- package/dist/src/{db → lib/db}/schema.d.ts +104 -1
- package/dist/src/{index.d.ts → lib/index.d.ts} +2 -2
- package/dist/src/{types.d.ts → lib/types.d.ts} +15 -14
- package/index.ts +1 -1
- package/package.json +8 -3
- package/src/{adapters → lib/adapters}/database.ts +20 -1
- package/src/{adapters → lib/adapters}/minio.ts +2 -1
- package/src/{adapters → lib/adapters}/mssql.ts +38 -2
- package/src/lib/ai/context/compressor.ts +259 -0
- package/src/{ai → lib/ai}/context/index.ts +6 -1
- package/src/lib/ai/context/intra-turn-compressor.ts +264 -0
- package/src/{ai → lib/ai}/context/types.ts +4 -1
- package/src/{ai → lib/ai}/index.ts +114 -13
- package/src/lib/ai/prompt.ts +354 -0
- package/src/{ai → lib/ai}/tools/execute-code.tool.ts +5 -2
- package/src/{auth → lib/auth}/middleware.ts +7 -0
- package/src/lib/config.ts +181 -0
- package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/20260326231647_nice_speedball}/migration.sql +12 -0
- package/src/{db/migrations/20260309012148_cloudy_maria_hill → lib/db/migrations/20260326231647_nice_speedball}/snapshot.json +106 -1
- package/src/{db → lib/db}/schema.ts +15 -2
- package/src/{factory.ts → lib/factory.ts} +1 -0
- package/src/{index.ts → lib/index.ts} +3 -1
- package/src/{routes → lib/routes}/files.ts +9 -2
- package/src/{routes → lib/routes}/threads.ts +8 -1
- package/src/{types.ts → lib/types.ts} +16 -15
- package/src/sample/db/client.ts +9 -0
- package/src/sample/db/migrations/20260411023125_rich_purple_man/migration.sql +5 -0
- package/src/sample/db/migrations/20260411023125_rich_purple_man/snapshot.json +50 -0
- package/src/sample/db/schema.ts +10 -0
- package/src/sample/domains/account/concepts/session.concept.ts +17 -0
- package/src/sample/domains/account/endpoints/listCurrentSessions.endpoint.ts +31 -0
- package/src/sample/domains/account/endpoints/revokeAllOtherSessions.endpoint.ts +22 -0
- package/src/sample/domains/account/endpoints/revokeCurrentSession.endpoint.ts +33 -0
- package/src/sample/domains/account/index.ts +18 -0
- package/src/sample/domains/appraisals/concepts/appraisalCycle.concept.ts +15 -0
- package/src/sample/domains/appraisals/concepts/appraisalRecord.concept.ts +20 -0
- package/src/sample/domains/appraisals/concepts/appraisalRecordCompetency.concept.ts +10 -0
- package/src/sample/domains/appraisals/concepts/appraisalRecordFunctionalRequirement.concept.ts +10 -0
- package/src/sample/domains/appraisals/concepts/appraisalRecordGoal.concept.ts +17 -0
- package/src/sample/domains/appraisals/endpoints/getAppraisalRecordById.endpoint.ts +321 -0
- package/src/sample/domains/appraisals/endpoints/getAppraisalRecordCompetencies.endpoint.ts +120 -0
- package/src/sample/domains/appraisals/endpoints/getAppraisalRecordFunctionalRequirements.endpoint.ts +126 -0
- package/src/sample/domains/appraisals/endpoints/getAppraisalRecordGoals.endpoint.ts +104 -0
- package/src/sample/domains/appraisals/endpoints/getAppraisalRecordScore.endpoint.ts +58 -0
- package/src/sample/domains/appraisals/endpoints/listAppraisalCyclesSimple.endpoint.ts +52 -0
- package/src/sample/domains/appraisals/endpoints/listAppraisalRecords.endpoint.ts +245 -0
- package/src/sample/domains/appraisals/endpoints/listAppraisalTemplatesSimple.endpoint.ts +52 -0
- package/src/sample/domains/appraisals/index.ts +40 -0
- package/src/sample/domains/appraisals/rules/appraisalRecordScoping.rule.ts +7 -0
- package/src/sample/domains/attendance/concepts/attendancePermission.concept.ts +17 -0
- package/src/sample/domains/attendance/concepts/attendanceTransaction.concept.ts +17 -0
- package/src/sample/domains/attendance/concepts/publicHoliday.concept.ts +10 -0
- package/src/sample/domains/attendance/concepts/punch.concept.ts +19 -0
- package/src/sample/domains/attendance/endpoints/getAttendanceTransactionById.endpoint.ts +444 -0
- package/src/sample/domains/attendance/endpoints/getCurrentAttendanceProfile.endpoint.ts +886 -0
- package/src/sample/domains/attendance/endpoints/getCurrentAttendanceStatistics.endpoint.ts +49 -0
- package/src/sample/domains/attendance/endpoints/listAttendancePermissions.endpoint.ts +176 -0
- package/src/sample/domains/attendance/endpoints/listAttendanceTransactionStates.endpoint.ts +26 -0
- package/src/sample/domains/attendance/endpoints/listAttendanceTransactions.endpoint.ts +373 -0
- package/src/sample/domains/attendance/endpoints/listPublicHolidaysSimple.endpoint.ts +52 -0
- package/src/sample/domains/attendance/endpoints/listPunchTypes.endpoint.ts +25 -0
- package/src/sample/domains/attendance/endpoints/listPunches.endpoint.ts +178 -0
- package/src/sample/domains/attendance/index.ts +41 -0
- package/src/sample/domains/attendance/services/attendancePermissions.service.ts +15 -0
- package/src/sample/domains/employees/concepts/employee.concept.ts +20 -0
- package/src/sample/domains/employees/concepts/employeeBankAccount.concept.ts +19 -0
- package/src/sample/domains/employees/concepts/employeeChild.concept.ts +19 -0
- package/src/sample/domains/employees/concepts/employeeDocument.concept.ts +21 -0
- package/src/sample/domains/employees/concepts/employeeEvent.concept.ts +21 -0
- package/src/sample/domains/employees/concepts/employeeExperience.concept.ts +18 -0
- package/src/sample/domains/employees/concepts/employeeJobLevelChange.concept.ts +21 -0
- package/src/sample/domains/employees/concepts/employeeMedal.concept.ts +20 -0
- package/src/sample/domains/employees/concepts/employeeQualification.concept.ts +20 -0
- package/src/sample/domains/employees/concepts/employeeSpouse.concept.ts +21 -0
- package/src/sample/domains/employees/concepts/employeeViolation.concept.ts +19 -0
- package/src/sample/domains/employees/endpoints/getCurrentEmployee.endpoint.ts +1651 -0
- package/src/sample/domains/employees/endpoints/getCurrentIdentity.endpoint.ts +197 -0
- package/src/sample/domains/employees/endpoints/getCurrentManagers.endpoint.ts +142 -0
- package/src/sample/domains/employees/endpoints/getEmployeeById.endpoint.ts +1659 -0
- package/src/sample/domains/employees/endpoints/getEmployeeStatistics.endpoint.ts +43 -0
- package/src/sample/domains/employees/endpoints/listBankAccounts.endpoint.ts +223 -0
- package/src/sample/domains/employees/endpoints/listChildren.endpoint.ts +211 -0
- package/src/sample/domains/employees/endpoints/listDocuments.endpoint.ts +222 -0
- package/src/sample/domains/employees/endpoints/listEmployeesSimple.endpoint.ts +156 -0
- package/src/sample/domains/employees/endpoints/listEvents.endpoint.ts +120 -0
- package/src/sample/domains/employees/endpoints/listExperiences.endpoint.ts +188 -0
- package/src/sample/domains/employees/endpoints/listJobLevelChanges.endpoint.ts +240 -0
- package/src/sample/domains/employees/endpoints/listMedals.endpoint.ts +266 -0
- package/src/sample/domains/employees/endpoints/listQualifications.endpoint.ts +265 -0
- package/src/sample/domains/employees/endpoints/listSpouses.endpoint.ts +278 -0
- package/src/sample/domains/employees/endpoints/listViolations.endpoint.ts +245 -0
- package/src/sample/domains/employees/index.ts +75 -0
- package/src/sample/domains/employees/services/employees.service.ts +181 -0
- package/src/sample/domains/index.ts +10 -0
- package/src/sample/domains/leaves/concepts/leave.concept.ts +21 -0
- package/src/sample/domains/leaves/concepts/leaveBalance.concept.ts +17 -0
- package/src/sample/domains/leaves/concepts/leaveBalanceTransaction.concept.ts +16 -0
- package/src/sample/domains/leaves/concepts/leaveType.concept.ts +13 -0
- package/src/sample/domains/leaves/concepts/leaveTypes.concept.ts +205 -0
- package/src/sample/domains/leaves/endpoints/getBalance.endpoint.ts +53 -0
- package/src/sample/domains/leaves/endpoints/getLeaveById.endpoint.ts +53 -0
- package/src/sample/domains/leaves/endpoints/listBalanceTransactions.endpoint.ts +196 -0
- package/src/sample/domains/leaves/endpoints/listBalanceTypes.endpoint.ts +26 -0
- package/src/sample/domains/leaves/endpoints/listBalances.endpoint.ts +201 -0
- package/src/sample/domains/leaves/endpoints/listLeaveTypes.endpoint.ts +28 -0
- package/src/sample/domains/leaves/endpoints/listLeaves.endpoint.ts +234 -0
- package/src/sample/domains/leaves/index.ts +38 -0
- package/src/sample/domains/leaves/services/leaveRequests.service.ts +232 -0
- package/src/sample/domains/notifications/concepts/notification.concept.ts +19 -0
- package/src/sample/domains/notifications/endpoints/countMyNotifications.endpoint.ts +21 -0
- package/src/sample/domains/notifications/endpoints/getMyNotification.endpoint.ts +57 -0
- package/src/sample/domains/notifications/endpoints/listMyNotifications.endpoint.ts +59 -0
- package/src/sample/domains/notifications/endpoints/markMyNotificationAsRead.endpoint.ts +32 -0
- package/src/sample/domains/notifications/index.ts +20 -0
- package/src/sample/domains/payroll/concepts/payslip.concept.ts +18 -0
- package/src/sample/domains/payroll/concepts/salaryCertificate.concept.ts +17 -0
- package/src/sample/domains/payroll/concepts/salaryCertificateRequestingEntity.concept.ts +8 -0
- package/src/sample/domains/payroll/endpoints/getPayslipById.endpoint.ts +238 -0
- package/src/sample/domains/payroll/endpoints/listPayslips.endpoint.ts +216 -0
- package/src/sample/domains/payroll/endpoints/listSalaryCertificateLanguages.endpoint.ts +25 -0
- package/src/sample/domains/payroll/endpoints/listSalaryCertificateRequestingEntities.endpoint.ts +62 -0
- package/src/sample/domains/payroll/endpoints/listSalaryCertificateTypes.endpoint.ts +24 -0
- package/src/sample/domains/payroll/endpoints/listSalaryCertificates.endpoint.ts +122 -0
- package/src/sample/domains/payroll/endpoints/previewSalaryCertificate.endpoint.ts +45 -0
- package/src/sample/domains/payroll/index.ts +32 -0
- package/src/sample/domains/payroll/rules/payrollEmployeeContext.rule.ts +7 -0
- package/src/sample/domains/payroll/services/salaryCertificates.service.ts +15 -0
- package/src/sample/domains/servicing/concepts/service.concept.ts +6 -0
- package/src/sample/domains/servicing/concepts/serviceRequest.concept.ts +12 -0
- package/src/sample/domains/servicing/concepts/serviceRequestApproval.concept.ts +9 -0
- package/src/sample/domains/servicing/endpoints/cancelServiceRequest.endpoint.ts +21 -0
- package/src/sample/domains/servicing/endpoints/createServiceRequest.endpoint.ts +511 -0
- package/src/sample/domains/servicing/endpoints/getServiceRequest.endpoint.ts +352 -0
- package/src/sample/domains/servicing/endpoints/getServiceRequestFormItems.endpoint.ts +219 -0
- package/src/sample/domains/servicing/endpoints/getServiceRequestTransactionFormItems.endpoint.ts +216 -0
- package/src/sample/domains/servicing/endpoints/getServiceStates.endpoint.ts +47 -0
- package/src/sample/domains/servicing/endpoints/invokeServiceRequestAction.endpoint.ts +139 -0
- package/src/sample/domains/servicing/endpoints/listServiceRequests.endpoint.ts +292 -0
- package/src/sample/domains/servicing/endpoints/listServices.endpoint.ts +108 -0
- package/src/sample/domains/servicing/endpoints/updateServiceRequest.endpoint.ts +355 -0
- package/src/sample/domains/servicing/index.ts +44 -0
- package/src/sample/domains/servicing/rules/checkValidActions.rule.ts +7 -0
- package/src/sample/domains/servicing/rules/createServiceRequest.rule.ts +7 -0
- package/src/sample/domains/servicing/rules/filterServiceRequestsByState.rule.ts +7 -0
- package/src/sample/domains/servicing/rules/passingOptionFields.rule.ts +7 -0
- package/src/sample/domains/shared/rules/pagination.rule.ts +7 -0
- package/src/sample/domains/suggestions/concepts/suggestion.concept.ts +19 -0
- package/src/sample/domains/suggestions/concepts/suggestionCategory.concept.ts +7 -0
- package/src/sample/domains/suggestions/concepts/suggestionImpactLevel.concept.ts +7 -0
- package/src/sample/domains/suggestions/endpoints/createSuggestion.endpoint.ts +705 -0
- package/src/sample/domains/suggestions/endpoints/deleteSuggestion.endpoint.ts +32 -0
- package/src/sample/domains/suggestions/endpoints/getSuggestionById.endpoint.ts +635 -0
- package/src/sample/domains/suggestions/endpoints/listSuggestionCategories.endpoint.ts +62 -0
- package/src/sample/domains/suggestions/endpoints/listSuggestionImpactLevels.endpoint.ts +62 -0
- package/src/sample/domains/suggestions/endpoints/listSuggestionStates.endpoint.ts +24 -0
- package/src/sample/domains/suggestions/endpoints/listSuggestions.endpoint.ts +132 -0
- package/src/sample/domains/suggestions/endpoints/updateSuggestion.endpoint.ts +690 -0
- package/src/sample/domains/suggestions/endpoints/updateSuggestionState.endpoint.ts +39 -0
- package/src/sample/domains/suggestions/index.ts +34 -0
- package/src/sample/domains/suggestions/rules/suggestionScope.rule.ts +7 -0
- package/src/sample/domains/surveys/concepts/survey.concept.ts +10 -0
- package/src/sample/domains/surveys/concepts/surveyResponse.concept.ts +14 -0
- package/src/sample/domains/surveys/endpoints/getSurveyResponseById.endpoint.ts +131 -0
- package/src/sample/domains/surveys/endpoints/getTargetedSurvey.endpoint.ts +129 -0
- package/src/sample/domains/surveys/endpoints/listTargetedSurveys.endpoint.ts +160 -0
- package/src/sample/domains/surveys/endpoints/respondToSurvey.endpoint.ts +144 -0
- package/src/sample/domains/surveys/index.ts +21 -0
- package/src/sample/drizzle.config.ts +11 -0
- package/src/sample/index.bak.ts +341 -0
- package/src/sample/index.ts +318 -0
- package/src/sample/official-statements/defaults.ts +57 -0
- package/src/sample/official-statements/routes.ts +137 -0
- package/src/sample/official-statements/service.ts +148 -0
- package/src/sample/official-statements/types.ts +23 -0
- package/dist/src/ai/context/compressor.d.ts +0 -7
- package/dist/src/ai/tools/call-endpoint.tool.d.ts +0 -7
- package/dist/src/config.d.ts +0 -159
- package/src/ai/context/compressor.ts +0 -47
- package/src/ai/prompt.ts +0 -126
- package/src/ai/tools/call-endpoint.tool.ts +0 -89
- package/src/config.ts +0 -164
- package/src/db/migrations/20260315000000_add_context_meta/migration.sql +0 -1
- package/dist/src/{adapters → lib/adapters}/minio.d.ts +0 -0
- package/dist/src/{adapters → lib/adapters}/mssql.d.ts +0 -0
- package/dist/src/{adapters → lib/adapters}/storage.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/active-streams.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/context/builder.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/context/summarizer.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/context/token-estimator.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/fetch.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/helpers.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/index.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/interceptors/request-interceptor.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/prompt.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/tools/capture-files.tool.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/tools/execute-code.tool.d.ts +0 -0
- package/dist/src/{ai → lib/ai}/tools/query-graph.tool.d.ts +0 -0
- package/dist/src/{auth → lib/auth}/middleware.d.ts +0 -0
- package/dist/src/{cli → lib/cli}/extract-endpoints.d.ts +0 -0
- package/dist/src/{db → lib/db}/migrate.d.ts +0 -0
- package/dist/src/{factory.d.ts → lib/factory.d.ts} +0 -0
- package/dist/src/{graph → lib/graph}/expand-domains.d.ts +0 -0
- package/dist/src/{graph → lib/graph}/generate-cypher.d.ts +0 -0
- package/dist/src/{graph → lib/graph}/helpers.d.ts +2 -2
- /package/dist/src/{graph → lib/graph}/index.d.ts +0 -0
- /package/dist/src/{graph → lib/graph}/neo4j.d.ts +0 -0
- /package/dist/src/{graph → lib/graph}/resolver.d.ts +0 -0
- /package/dist/src/{graph → lib/graph}/seed.d.ts +0 -0
- /package/dist/src/{graph → lib/graph}/types.d.ts +0 -0
- /package/dist/src/{graph → lib/graph}/validate.d.ts +0 -0
- /package/dist/src/{routes → lib/routes}/chat.d.ts +0 -0
- /package/dist/src/{routes → lib/routes}/files.d.ts +0 -0
- /package/dist/src/{routes → lib/routes}/index.d.ts +0 -0
- /package/dist/src/{routes → lib/routes}/threads.d.ts +0 -0
- /package/dist/src/{routes → lib/routes}/ws.d.ts +0 -0
- /package/dist/src/{ws → lib/ws}/connections.d.ts +0 -0
- /package/dist/src/{ws → lib/ws}/events.d.ts +0 -0
- /package/dist/src/{ws → lib/ws}/index.d.ts +0 -0
- /package/dist/src/{ws → lib/ws}/notify.d.ts +0 -0
- /package/src/{adapters → lib/adapters}/storage.ts +0 -0
- /package/src/{ai → lib/ai}/active-streams.ts +0 -0
- /package/src/{ai → lib/ai}/context/builder.ts +0 -0
- /package/src/{ai → lib/ai}/context/summarizer.ts +0 -0
- /package/src/{ai → lib/ai}/context/token-estimator.ts +0 -0
- /package/src/{ai → lib/ai}/fetch.ts +0 -0
- /package/src/{ai → lib/ai}/helpers.ts +0 -0
- /package/src/{ai → lib/ai}/interceptors/request-interceptor.ts +0 -0
- /package/src/{ai → lib/ai}/tools/capture-files.tool.ts +0 -0
- /package/src/{ai → lib/ai}/tools/query-graph.tool.ts +0 -0
- /package/src/{cli → lib/cli}/extract-endpoints.ts +0 -0
- /package/src/{db → lib/db}/migrate.ts +0 -0
- /package/src/{graph → lib/graph}/expand-domains.ts +0 -0
- /package/src/{graph → lib/graph}/generate-cypher.ts +0 -0
- /package/src/{graph → lib/graph}/helpers.ts +0 -0
- /package/src/{graph → lib/graph}/index.ts +0 -0
- /package/src/{graph → lib/graph}/neo4j.ts +0 -0
- /package/src/{graph → lib/graph}/resolver.ts +0 -0
- /package/src/{graph → lib/graph}/seed.ts +0 -0
- /package/src/{graph → lib/graph}/types.ts +0 -0
- /package/src/{graph → lib/graph}/validate.ts +0 -0
- /package/src/{routes → lib/routes}/chat.ts +0 -0
- /package/src/{routes → lib/routes}/index.ts +0 -0
- /package/src/{routes → lib/routes}/ws.ts +0 -0
- /package/src/{ws → lib/ws}/connections.ts +0 -0
- /package/src/{ws → lib/ws}/events.ts +0 -0
- /package/src/{ws → lib/ws}/index.ts +0 -0
- /package/src/{ws → lib/ws}/notify.ts +0 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import type { UIMessage } from "ai";
|
|
2
|
+
import { estimateTokens, CHARS_PER_TOKEN } from "./token-estimator.ts";
|
|
3
|
+
import type { MessageMetadata } from "../../types.ts";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns a new array of messages with large tool outputs truncated
|
|
7
|
+
* to `maxTokensPerResult`. Does not mutate the input messages.
|
|
8
|
+
*/
|
|
9
|
+
export function compressToolResults(
|
|
10
|
+
messages: UIMessage<MessageMetadata>[],
|
|
11
|
+
maxTokensPerResult: number,
|
|
12
|
+
) {
|
|
13
|
+
return messages.map((message) => {
|
|
14
|
+
let hasLargeToolOutput = false;
|
|
15
|
+
|
|
16
|
+
for (const part of message.parts) {
|
|
17
|
+
if ("toolCallId" in part && "output" in part && part.output != null) {
|
|
18
|
+
const outputTokens = estimateTokens(JSON.stringify(part.output));
|
|
19
|
+
if (outputTokens > maxTokensPerResult) {
|
|
20
|
+
hasLargeToolOutput = true;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (!hasLargeToolOutput) return message;
|
|
27
|
+
|
|
28
|
+
const compressedParts = message.parts.map((part) => {
|
|
29
|
+
if (!("toolCallId" in part) || !("output" in part) || part.output == null) {
|
|
30
|
+
return part;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const outputStr = JSON.stringify(part.output);
|
|
34
|
+
const outputTokens = estimateTokens(outputStr);
|
|
35
|
+
|
|
36
|
+
if (outputTokens <= maxTokensPerResult) return part;
|
|
37
|
+
|
|
38
|
+
const charBudget = maxTokensPerResult * CHARS_PER_TOKEN;
|
|
39
|
+
const truncatedOutput = smartStructuralCompress(part.output, charBudget);
|
|
40
|
+
|
|
41
|
+
return { ...part, output: truncatedOutput } as typeof part;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return { ...message, parts: compressedParts };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Structurally compresses a value to fit within a character budget.
|
|
50
|
+
* Understands JSON arrays, error objects, and nested objects — preserving
|
|
51
|
+
* semantic meaning better than naive string truncation.
|
|
52
|
+
*/
|
|
53
|
+
export function smartStructuralCompress(value: unknown, charBudget: number) {
|
|
54
|
+
if (typeof value === "string") {
|
|
55
|
+
return compressString(value, charBudget);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
return compressArray(value, charBudget);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (typeof value === "object" && value !== null) {
|
|
63
|
+
return compressObject(value as Record<string, unknown>, charBudget);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Primitives
|
|
67
|
+
const str = String(value);
|
|
68
|
+
if (str.length <= charBudget) return str;
|
|
69
|
+
return str.slice(0, charBudget - 20) + "\n[...truncated]";
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function compressString(value: string, charBudget: number) {
|
|
73
|
+
if (value.length <= charBudget) return value;
|
|
74
|
+
|
|
75
|
+
// Try to parse as JSON first — if it's a stringified structure, compress structurally
|
|
76
|
+
try {
|
|
77
|
+
const parsed: unknown = JSON.parse(value);
|
|
78
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
79
|
+
const result = smartStructuralCompress(parsed, charBudget) as string;
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// Not JSON, just truncate
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return value.slice(0, charBudget - 20) + "\n[...truncated]";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function compressArray(value: unknown[], charBudget: number) {
|
|
90
|
+
const totalCount = value.length;
|
|
91
|
+
|
|
92
|
+
if (totalCount === 0) return "[]";
|
|
93
|
+
|
|
94
|
+
// Infer schema from first object element (if items are objects)
|
|
95
|
+
const firstObj = value.find(
|
|
96
|
+
(item): item is Record<string, unknown> =>
|
|
97
|
+
typeof item === "object" && item !== null && !Array.isArray(item),
|
|
98
|
+
);
|
|
99
|
+
const schemaKeys = firstObj ? Object.keys(firstObj) : null;
|
|
100
|
+
|
|
101
|
+
// Keep as many items as fit within ~60% of budget, leaving room for the summary footer
|
|
102
|
+
const itemBudget = Math.floor(charBudget * 0.6);
|
|
103
|
+
const items: unknown[] = [];
|
|
104
|
+
let accumulated = 0;
|
|
105
|
+
|
|
106
|
+
for (const item of value) {
|
|
107
|
+
const itemStr = JSON.stringify(item);
|
|
108
|
+
if (accumulated + itemStr.length > itemBudget && items.length > 0) break;
|
|
109
|
+
accumulated += itemStr.length;
|
|
110
|
+
items.push(item);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const shownStr = JSON.stringify(items, null, 2);
|
|
114
|
+
const remaining = totalCount - items.length;
|
|
115
|
+
|
|
116
|
+
if (remaining === 0 && shownStr.length <= charBudget) return shownStr;
|
|
117
|
+
|
|
118
|
+
const schemaHint = schemaKeys ? ` Schema: {${schemaKeys.join(", ")}}` : "";
|
|
119
|
+
const footer = `\n[COMPRESSED: ${remaining} more items of ${totalCount} total not shown.${schemaHint} Re-execute with filters or pagination to access specific items.]`;
|
|
120
|
+
|
|
121
|
+
const result = shownStr + footer;
|
|
122
|
+
if (result.length <= charBudget) return result;
|
|
123
|
+
|
|
124
|
+
// If still too large, fall back to a tighter representation
|
|
125
|
+
const tightStr = JSON.stringify(items);
|
|
126
|
+
return tightStr.slice(0, charBudget - footer.length - 20) + footer;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function compressObject(value: Record<string, unknown>, charBudget: number) {
|
|
130
|
+
// Check if it looks like an error — keep message, drop stack
|
|
131
|
+
if (isErrorLike(value)) {
|
|
132
|
+
return compressErrorObject(value, charBudget);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const fullStr = JSON.stringify(value, null, 2);
|
|
136
|
+
if (fullStr.length <= charBudget) return fullStr;
|
|
137
|
+
|
|
138
|
+
const allKeys = Object.keys(value);
|
|
139
|
+
|
|
140
|
+
// Phase 1: Partition keys into scalar (cheap) and complex (expensive)
|
|
141
|
+
const scalarEntries: string[] = [];
|
|
142
|
+
const complexKeys: string[] = [];
|
|
143
|
+
let scalarCharsUsed = 0;
|
|
144
|
+
|
|
145
|
+
for (const key of allKeys) {
|
|
146
|
+
const val = value[key];
|
|
147
|
+
const valStr = JSON.stringify(val);
|
|
148
|
+
|
|
149
|
+
if (valStr === undefined) {
|
|
150
|
+
const entry = ` "${key}": null`;
|
|
151
|
+
scalarEntries.push(entry);
|
|
152
|
+
scalarCharsUsed += entry.length + 2; // +2 for comma and newline
|
|
153
|
+
} else if (isScalar(val)) {
|
|
154
|
+
const entry = ` "${key}": ${valStr}`;
|
|
155
|
+
scalarEntries.push(entry);
|
|
156
|
+
scalarCharsUsed += entry.length + 2;
|
|
157
|
+
} else {
|
|
158
|
+
complexKeys.push(key);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Phase 2: Distribute remaining budget to complex values
|
|
163
|
+
const compressionHeader = ` "__compressed": "Some values were compressed. If you need the full data for a specific key, re-execute the tool returning only that key."`;
|
|
164
|
+
const headerCost = compressionHeader.length + 2;
|
|
165
|
+
const structuralOverhead = 4; // { } and newlines
|
|
166
|
+
const remainingBudget = charBudget - scalarCharsUsed - headerCost - structuralOverhead;
|
|
167
|
+
const complexBudget = Math.floor(
|
|
168
|
+
Math.max(remainingBudget, 100) / Math.max(complexKeys.length, 1),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const complexEntries: string[] = [];
|
|
172
|
+
const truncatedKeys: string[] = [];
|
|
173
|
+
|
|
174
|
+
for (const key of complexKeys) {
|
|
175
|
+
const val = value[key];
|
|
176
|
+
const valStr = JSON.stringify(val);
|
|
177
|
+
|
|
178
|
+
if (valStr !== undefined && valStr.length <= complexBudget) {
|
|
179
|
+
complexEntries.push(` "${key}": ${valStr}`);
|
|
180
|
+
} else {
|
|
181
|
+
truncatedKeys.push(key);
|
|
182
|
+
complexEntries.push(` "${key}": ${summarizeValue(val)}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Build result with compression notice
|
|
187
|
+
const allEntries = [...scalarEntries, ...complexEntries];
|
|
188
|
+
|
|
189
|
+
if (truncatedKeys.length > 0) {
|
|
190
|
+
allEntries.push(compressionHeader);
|
|
191
|
+
allEntries.push(` "__truncatedKeys": ${JSON.stringify(truncatedKeys)}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const result = `{\n${allEntries.join(",\n")}\n}`;
|
|
195
|
+
if (result.length <= charBudget) return result;
|
|
196
|
+
|
|
197
|
+
// Final fallback: keep scalars + summaries, trim from the complex entries end
|
|
198
|
+
return result.slice(0, charBudget - 20) + "\n[...truncated]";
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function isScalar(val: unknown): boolean {
|
|
202
|
+
return (
|
|
203
|
+
val === null ||
|
|
204
|
+
typeof val === "string" ||
|
|
205
|
+
typeof val === "number" ||
|
|
206
|
+
typeof val === "boolean"
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function isErrorLike(value: Record<string, unknown>) {
|
|
211
|
+
return "error" in value || "message" in value || "stack" in value || "stackTrace" in value;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function compressErrorObject(value: Record<string, unknown>, charBudget: number) {
|
|
215
|
+
// Keep error/message fields, drop stack traces
|
|
216
|
+
const { stack: _stack, stackTrace: _stackTrace, ...rest } = value;
|
|
217
|
+
const compressed = JSON.stringify(rest, null, 2);
|
|
218
|
+
|
|
219
|
+
if (compressed.length <= charBudget) return compressed;
|
|
220
|
+
return compressed.slice(0, charBudget - 20) + "\n[...truncated]";
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Produces a short human-readable summary of a value's shape and key data.
|
|
225
|
+
* Used both for inline object-key placeholders and for tool result one-liners.
|
|
226
|
+
*/
|
|
227
|
+
export function summarizeValue(val: unknown) {
|
|
228
|
+
if (Array.isArray(val)) {
|
|
229
|
+
const count = val.length;
|
|
230
|
+
const firstItem = val[0];
|
|
231
|
+
const schemaHint =
|
|
232
|
+
typeof firstItem === "object" && firstItem !== null
|
|
233
|
+
? ` {${Object.keys(firstItem).slice(0, 5).join(", ")}}`
|
|
234
|
+
: "";
|
|
235
|
+
return `[Array(${count})${schemaHint}]`;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (typeof val === "object" && val !== null) {
|
|
239
|
+
const obj = val as Record<string, unknown>;
|
|
240
|
+
const keys = Object.keys(obj);
|
|
241
|
+
|
|
242
|
+
// Detect common status/success response patterns
|
|
243
|
+
if ("status" in obj || "success" in obj) {
|
|
244
|
+
const status = obj["status"] ?? obj["success"];
|
|
245
|
+
const dataKey = keys.find((k) => k === "data" || k === "result" || k === "results");
|
|
246
|
+
const dataVal = dataKey ? obj[dataKey] : null;
|
|
247
|
+
const dataSummary = Array.isArray(dataVal) ? `, ${dataVal.length} items` : "";
|
|
248
|
+
return `{${String(status)}${dataSummary}}`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return `{Object(${keys.length} keys: ${keys.slice(0, 5).join(", ")}${keys.length > 5 ? ", ..." : ""})}`;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (typeof val === "string") {
|
|
255
|
+
return val.length > 80 ? `"${val.slice(0, 77)}..."` : JSON.stringify(val);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return String(val);
|
|
259
|
+
}
|
|
@@ -6,9 +6,14 @@ export {
|
|
|
6
6
|
estimateMessageTokens,
|
|
7
7
|
estimateMessagesTokens,
|
|
8
8
|
} from "./token-estimator.ts";
|
|
9
|
-
export { compressToolResults } from "./compressor.ts";
|
|
9
|
+
export { compressToolResults, smartStructuralCompress, summarizeValue } from "./compressor.ts";
|
|
10
10
|
export { summarizeMessages } from "./summarizer.ts";
|
|
11
11
|
export { buildContextMessages, trimMessagesToFit } from "./builder.ts";
|
|
12
|
+
export {
|
|
13
|
+
compressIntraTurnToolResults,
|
|
14
|
+
estimateToolResultTokens,
|
|
15
|
+
summarizeOldStepResults,
|
|
16
|
+
} from "./intra-turn-compressor.ts";
|
|
12
17
|
|
|
13
18
|
import type { UIMessage } from "ai";
|
|
14
19
|
import type { ResolvedCortexAgentConfig } from "../../config.ts";
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV3Prompt,
|
|
3
|
+
LanguageModelV3Message,
|
|
4
|
+
LanguageModelV3ToolResultPart,
|
|
5
|
+
} from "@ai-sdk/provider";
|
|
6
|
+
import { generateText } from "ai";
|
|
7
|
+
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
|
|
8
|
+
import { estimateTokens } from "./token-estimator.ts";
|
|
9
|
+
import { summarizeValue } from "./compressor.ts";
|
|
10
|
+
import type { ContextConfig } from "./types.ts";
|
|
11
|
+
|
|
12
|
+
type ToolRoleMessage = LanguageModelV3Message & { role: "tool" };
|
|
13
|
+
|
|
14
|
+
type SummarizationModelConfig = NonNullable<ContextConfig["summarizationModel"]>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Compresses tool results from older steps in the prompt.
|
|
18
|
+
*
|
|
19
|
+
* On step N, tool results from steps 1..N-2 are aggressively compressed
|
|
20
|
+
* to short one-liners. Only the most recent tool result stays in full.
|
|
21
|
+
* Does not mutate the input — returns a new prompt array.
|
|
22
|
+
*/
|
|
23
|
+
export function compressIntraTurnToolResults(
|
|
24
|
+
prompt: LanguageModelV3Prompt,
|
|
25
|
+
toolResultMaxTokens: number,
|
|
26
|
+
) {
|
|
27
|
+
// Collect indices of all tool-role messages
|
|
28
|
+
const toolMessageIndices: number[] = [];
|
|
29
|
+
for (let i = 0; i < prompt.length; i++) {
|
|
30
|
+
if (prompt[i]!.role === "tool") {
|
|
31
|
+
toolMessageIndices.push(i);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Need at least 3 tool messages to have "old" ones to compress
|
|
36
|
+
// (keep last 1 fully intact, compress the rest)
|
|
37
|
+
if (toolMessageIndices.length < 3) return prompt;
|
|
38
|
+
|
|
39
|
+
// Clone the prompt so we don't mutate the original
|
|
40
|
+
const result = prompt.map((msg) => ({ ...msg }));
|
|
41
|
+
|
|
42
|
+
// Compress all tool messages except the last one
|
|
43
|
+
const indicesToCompress = toolMessageIndices.slice(0, -1);
|
|
44
|
+
|
|
45
|
+
for (const idx of indicesToCompress) {
|
|
46
|
+
const msg = result[idx] as ToolRoleMessage;
|
|
47
|
+
const compressedContent = msg.content.map((part) => {
|
|
48
|
+
if (part.type !== "tool-result") return part;
|
|
49
|
+
return compressToolResultPart(part, toolResultMaxTokens);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
result[idx] = { ...msg, content: compressedContent } as LanguageModelV3Message;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function compressToolResultPart(
|
|
59
|
+
part: LanguageModelV3ToolResultPart,
|
|
60
|
+
maxTokens: number,
|
|
61
|
+
): LanguageModelV3ToolResultPart {
|
|
62
|
+
const output = part.output;
|
|
63
|
+
|
|
64
|
+
// Already small enough
|
|
65
|
+
const outputStr = stringifyOutput(output);
|
|
66
|
+
if (estimateTokens(outputStr) <= maxTokens) return part;
|
|
67
|
+
|
|
68
|
+
const compressed = compressOutputToOneLiner(output, part.toolName);
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
...part,
|
|
72
|
+
output: { type: "text", value: compressed },
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function stringifyOutput(output: LanguageModelV3ToolResultPart["output"]): string {
|
|
77
|
+
switch (output.type) {
|
|
78
|
+
case "text":
|
|
79
|
+
case "error-text":
|
|
80
|
+
return output.value;
|
|
81
|
+
case "json":
|
|
82
|
+
case "error-json":
|
|
83
|
+
return JSON.stringify(output.value);
|
|
84
|
+
case "execution-denied":
|
|
85
|
+
return output.reason ?? "denied";
|
|
86
|
+
case "content":
|
|
87
|
+
return JSON.stringify(output.value);
|
|
88
|
+
default:
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function compressOutputToOneLiner(
|
|
94
|
+
output: LanguageModelV3ToolResultPart["output"],
|
|
95
|
+
toolName: string,
|
|
96
|
+
): string {
|
|
97
|
+
const tag = `[COMPRESSED prior ${toolName} result]`;
|
|
98
|
+
|
|
99
|
+
switch (output.type) {
|
|
100
|
+
case "text": {
|
|
101
|
+
const preview = extractPreview(output.value);
|
|
102
|
+
return `${tag} ${preview}`;
|
|
103
|
+
}
|
|
104
|
+
case "json":
|
|
105
|
+
return `${tag} ${summarizeValue(output.value)}`;
|
|
106
|
+
case "error-text": {
|
|
107
|
+
const firstLine = output.value.split("\n")[0] ?? "error";
|
|
108
|
+
return `${tag} ERROR - ${firstLine}`;
|
|
109
|
+
}
|
|
110
|
+
case "error-json": {
|
|
111
|
+
const errStr = JSON.stringify(output.value).slice(0, 200);
|
|
112
|
+
return `${tag} ERROR - ${errStr}`;
|
|
113
|
+
}
|
|
114
|
+
case "execution-denied":
|
|
115
|
+
return `${tag} execution denied${output.reason ? ` - ${output.reason}` : ""}`;
|
|
116
|
+
case "content": {
|
|
117
|
+
const textParts = output.value
|
|
118
|
+
.filter((p): p is Extract<typeof p, { type: "text" }> => p.type === "text")
|
|
119
|
+
.map((p) => p.text);
|
|
120
|
+
const joined = textParts.join(" ");
|
|
121
|
+
const preview = extractPreview(joined);
|
|
122
|
+
return `${tag} ${preview}`;
|
|
123
|
+
}
|
|
124
|
+
default:
|
|
125
|
+
return `${tag} completed`;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function extractPreview(text: string): string {
|
|
130
|
+
// Try to parse as JSON for structured summary
|
|
131
|
+
try {
|
|
132
|
+
const parsed: unknown = JSON.parse(text);
|
|
133
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
134
|
+
return summarizeValue(parsed);
|
|
135
|
+
}
|
|
136
|
+
} catch {
|
|
137
|
+
// Not JSON
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (text.length <= 150) return text;
|
|
141
|
+
return text.slice(0, 147) + "...";
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Estimates the total token count of tool results in the prompt.
|
|
146
|
+
*/
|
|
147
|
+
export function estimateToolResultTokens(prompt: LanguageModelV3Prompt): number {
|
|
148
|
+
let total = 0;
|
|
149
|
+
|
|
150
|
+
for (const msg of prompt) {
|
|
151
|
+
if (msg.role !== "tool") continue;
|
|
152
|
+
for (const part of msg.content) {
|
|
153
|
+
if (part.type === "tool-result") {
|
|
154
|
+
total += estimateTokens(stringifyOutput(part.output));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return total;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* When accumulated tool result tokens exceed the threshold, summarizes
|
|
164
|
+
* older step results using a lightweight LLM call.
|
|
165
|
+
*
|
|
166
|
+
* Replaces all tool results except the last two with a single synthetic
|
|
167
|
+
* summary in the oldest tool message.
|
|
168
|
+
*/
|
|
169
|
+
export async function summarizeOldStepResults(
|
|
170
|
+
prompt: LanguageModelV3Prompt,
|
|
171
|
+
thresholdTokens: number,
|
|
172
|
+
modelConfig: SummarizationModelConfig,
|
|
173
|
+
) {
|
|
174
|
+
const totalToolTokens = estimateToolResultTokens(prompt);
|
|
175
|
+
if (totalToolTokens <= thresholdTokens) return prompt;
|
|
176
|
+
|
|
177
|
+
// Collect tool-role message indices
|
|
178
|
+
const toolMessageIndices: number[] = [];
|
|
179
|
+
for (let i = 0; i < prompt.length; i++) {
|
|
180
|
+
if (prompt[i]!.role === "tool") {
|
|
181
|
+
toolMessageIndices.push(i);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Keep the last 2 tool messages intact, summarize the rest
|
|
186
|
+
if (toolMessageIndices.length < 3) return prompt;
|
|
187
|
+
|
|
188
|
+
const indicesToSummarize = toolMessageIndices.slice(0, -2);
|
|
189
|
+
|
|
190
|
+
// Collect tool result text from older steps
|
|
191
|
+
const resultTexts: string[] = [];
|
|
192
|
+
for (const idx of indicesToSummarize) {
|
|
193
|
+
const msg = prompt[idx] as ToolRoleMessage;
|
|
194
|
+
for (const part of msg.content) {
|
|
195
|
+
if (part.type === "tool-result") {
|
|
196
|
+
resultTexts.push(`[${part.toolName}]: ${stringifyOutput(part.output)}`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (resultTexts.length === 0) return prompt;
|
|
202
|
+
|
|
203
|
+
// Summarize with LLM
|
|
204
|
+
const provider = createOpenAICompatible({
|
|
205
|
+
name: modelConfig.providerName ?? "summarization-provider",
|
|
206
|
+
baseURL: modelConfig.baseURL,
|
|
207
|
+
apiKey: modelConfig.apiKey,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const model = provider.chatModel(modelConfig.modelName);
|
|
211
|
+
|
|
212
|
+
const { text: summary } = await generateText({
|
|
213
|
+
model,
|
|
214
|
+
system: `You are a tool result summarizer. Given previous tool call results from an AI agent's workflow, produce a concise summary preserving:
|
|
215
|
+
- Key data values, IDs, and counts
|
|
216
|
+
- Success/failure status of each operation
|
|
217
|
+
- Any error messages
|
|
218
|
+
- Data that subsequent tool calls may need
|
|
219
|
+
|
|
220
|
+
Maximum 300 tokens. Use bullet points. No preamble.`,
|
|
221
|
+
prompt: `Summarize these tool results:\n\n${resultTexts.join("\n\n")}`,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Replace old tool messages with collapsed versions containing just the summary
|
|
225
|
+
const result = [...prompt];
|
|
226
|
+
|
|
227
|
+
for (let i = 0; i < indicesToSummarize.length; i++) {
|
|
228
|
+
const idx = indicesToSummarize[i]!;
|
|
229
|
+
const originalMsg = prompt[idx] as ToolRoleMessage;
|
|
230
|
+
|
|
231
|
+
if (i === 0) {
|
|
232
|
+
// First old tool message gets the summary
|
|
233
|
+
const summarizedContent = originalMsg.content.map((part) => {
|
|
234
|
+
if (part.type !== "tool-result") return part;
|
|
235
|
+
return {
|
|
236
|
+
...part,
|
|
237
|
+
output: {
|
|
238
|
+
type: "text" as const,
|
|
239
|
+
value: `[Summary of ${resultTexts.length} earlier tool results]:\n${summary}`,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Keep only the first tool-result part with the summary
|
|
245
|
+
const firstResultIdx = summarizedContent.findIndex((p) => p.type === "tool-result");
|
|
246
|
+
const collapsedContent =
|
|
247
|
+
firstResultIdx >= 0 ? [summarizedContent[firstResultIdx]!] : summarizedContent;
|
|
248
|
+
|
|
249
|
+
result[idx] = { ...originalMsg, content: collapsedContent } as LanguageModelV3Message;
|
|
250
|
+
} else {
|
|
251
|
+
// Subsequent old tool messages get one-liners
|
|
252
|
+
const collapsedContent = originalMsg.content.map((part) => {
|
|
253
|
+
if (part.type !== "tool-result") return part;
|
|
254
|
+
return {
|
|
255
|
+
...part,
|
|
256
|
+
output: { type: "text" as const, value: `[see summary above]` },
|
|
257
|
+
};
|
|
258
|
+
});
|
|
259
|
+
result[idx] = { ...originalMsg, content: collapsedContent } as LanguageModelV3Message;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
@@ -10,6 +10,8 @@ export type ContextConfig = {
|
|
|
10
10
|
};
|
|
11
11
|
toolResultMaxTokens: number;
|
|
12
12
|
recentMessagesToKeep: number;
|
|
13
|
+
/** Token threshold for triggering intra-turn summarization of older tool results */
|
|
14
|
+
intraTurnSummarizationThresholdTokens: number;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
17
|
export type ThreadContextMeta = {
|
|
@@ -23,6 +25,7 @@ export const DEFAULT_CONTEXT_CONFIG: ContextConfig = {
|
|
|
23
25
|
maxContextTokens: 120_000,
|
|
24
26
|
reservedTokenBudget: 8_000,
|
|
25
27
|
summarizationThreshold: 0.75,
|
|
26
|
-
toolResultMaxTokens:
|
|
28
|
+
toolResultMaxTokens: 1_000,
|
|
27
29
|
recentMessagesToKeep: 6,
|
|
30
|
+
intraTurnSummarizationThresholdTokens: 20_000,
|
|
28
31
|
};
|