nolo-cli 0.1.8 → 0.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/README.md +32 -0
  2. package/agentRuntimeCommands.ts +3 -3
  3. package/ai/agent/_executeModel.ts +118 -0
  4. package/ai/agent/agentSlice.ts +525 -0
  5. package/ai/agent/appWorkingMemory.ts +126 -0
  6. package/ai/agent/avatarUtils.ts +24 -0
  7. package/ai/agent/buildEditingContext.ts +373 -0
  8. package/ai/agent/buildSystemPrompt.ts +532 -0
  9. package/ai/agent/cleanAgentMessages.ts +140 -0
  10. package/ai/agent/cliChatClient.ts +119 -0
  11. package/ai/agent/cliExecutor.ts +733 -0
  12. package/ai/agent/cliPrompt.ts +10 -0
  13. package/ai/agent/contextCompiler.ts +107 -0
  14. package/ai/agent/contextLayerContract.ts +44 -0
  15. package/ai/agent/createAgentSchema.ts +234 -0
  16. package/ai/agent/executeToolCall.ts +58 -0
  17. package/ai/agent/fetchAgentContexts.ts +42 -0
  18. package/ai/agent/generatePrompt.ts +3 -0
  19. package/ai/agent/getFullChatContextKeys.ts +168 -0
  20. package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
  21. package/ai/agent/hooks/useAgentConfig.ts +61 -0
  22. package/ai/agent/hooks/useAgentDialog.ts +35 -0
  23. package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
  24. package/ai/agent/hooks/usePublicAgents.ts +473 -0
  25. package/ai/agent/machineRunPermissions.ts +95 -0
  26. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  27. package/ai/agent/planSlice.ts +259 -0
  28. package/ai/agent/referenceUtils.ts +229 -0
  29. package/ai/agent/runAgentBackground.ts +238 -0
  30. package/ai/agent/runAgentClientLoop.ts +138 -0
  31. package/ai/agent/runtimeGuidance.ts +97 -0
  32. package/ai/agent/runtimeServerBase.ts +37 -0
  33. package/ai/agent/server/fetchPublicAgents.ts +128 -0
  34. package/ai/agent/startParallelAgentStreams.ts +424 -0
  35. package/ai/agent/startupProtocol.ts +53 -0
  36. package/ai/agent/streamAgentChatTurn.ts +1278 -0
  37. package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
  38. package/ai/agent/types.ts +71 -0
  39. package/ai/agent/utils/imageOutput.ts +33 -0
  40. package/ai/agent/utils/sortUtils.ts +250 -0
  41. package/ai/agent/web/referencePickerUtils.ts +146 -0
  42. package/ai/ai.locale.ts +1075 -0
  43. package/ai/chat/accumulateToolCallChunks.ts +95 -0
  44. package/ai/chat/fetchUtils.native.ts +276 -0
  45. package/ai/chat/fetchUtils.ts +153 -0
  46. package/ai/chat/parseApiError.ts +64 -0
  47. package/ai/chat/parseMultilineSSE.ts +95 -0
  48. package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
  49. package/ai/chat/sendOpenAICompletionsRequest.ts +703 -0
  50. package/ai/chat/sendOpenAIResponseRequest.ts +491 -0
  51. package/ai/chat/shouldUseServerProxy.ts +18 -0
  52. package/ai/chat/sseClient.native.ts +91 -0
  53. package/ai/chat/sseClient.ts +67 -0
  54. package/ai/chat/streamReader.native.ts +31 -0
  55. package/ai/chat/streamReader.ts +62 -0
  56. package/ai/chat/updateTotalUsage.ts +72 -0
  57. package/ai/context/buildReferenceContext.ts +437 -0
  58. package/ai/context/calculateContextUsage.ts +133 -0
  59. package/ai/context/retention.ts +165 -0
  60. package/ai/context/tokenUtils.ts +78 -0
  61. package/ai/index.ts +1 -0
  62. package/ai/llm/calculateGeminiImageTokens.ts +57 -0
  63. package/ai/llm/deepinfra.ts +28 -0
  64. package/ai/llm/fireworks.ts +50 -0
  65. package/ai/llm/generateRequestBody.ts +165 -0
  66. package/ai/llm/getModelContextWindow.ts +84 -0
  67. package/ai/llm/getNoloKey.ts +31 -0
  68. package/ai/llm/getPricing.ts +199 -0
  69. package/ai/llm/hooks/useModelPricing.ts +75 -0
  70. package/ai/llm/imagePricing.ts +40 -0
  71. package/ai/llm/isResponseAPIModel.ts +13 -0
  72. package/ai/llm/mimo.ts +71 -0
  73. package/ai/llm/mistral.ts +22 -0
  74. package/ai/llm/modelAvatar.ts +427 -0
  75. package/ai/llm/models.ts +45 -0
  76. package/ai/llm/openrouterModels.ts +269 -0
  77. package/ai/llm/providers.ts +306 -0
  78. package/ai/llm/reasoningModels.ts +28 -0
  79. package/ai/llm/types.ts +59 -0
  80. package/ai/llm/usageRequestOptions.ts +59 -0
  81. package/ai/memory/capture.ts +148 -0
  82. package/ai/memory/consolidate.ts +104 -0
  83. package/ai/memory/delete.ts +147 -0
  84. package/ai/memory/overlay.ts +84 -0
  85. package/ai/memory/query.ts +38 -0
  86. package/ai/memory/queryShared.ts +160 -0
  87. package/ai/memory/rank.ts +105 -0
  88. package/ai/memory/recentRelationshipRecap.ts +249 -0
  89. package/ai/memory/remember.ts +167 -0
  90. package/ai/memory/runtime.ts +76 -0
  91. package/ai/memory/store.ts +20 -0
  92. package/ai/memory/storeShared.ts +76 -0
  93. package/ai/memory/types.ts +46 -0
  94. package/ai/memory/understanding.ts +349 -0
  95. package/ai/memory/understandingGreeting.ts +264 -0
  96. package/ai/messages/type.ts +20 -0
  97. package/ai/policy/personalizationDialog.ts +333 -0
  98. package/ai/policy/runtimePolicy.ts +440 -0
  99. package/ai/policy/selfUpdateFields.ts +48 -0
  100. package/ai/policy/types.ts +64 -0
  101. package/ai/skills/referenceRuntime.ts +274 -0
  102. package/ai/skills/skillDiagnostics.ts +251 -0
  103. package/ai/skills/skillDocBuilder.ts +139 -0
  104. package/ai/skills/skillDocProtocol.ts +434 -0
  105. package/ai/skills/skillReferenceSummary.ts +63 -0
  106. package/ai/skills/skillSummaryMarker.ts +26 -0
  107. package/ai/token/calculatePrice.ts +544 -0
  108. package/ai/token/db.ts +98 -0
  109. package/ai/token/externalToolCost.ts +330 -0
  110. package/ai/token/hooks/useRecords.ts +65 -0
  111. package/ai/token/missingUsageEstimate.ts +42 -0
  112. package/ai/token/modelUsageQuery.ts +252 -0
  113. package/ai/token/normalizeUsage.ts +84 -0
  114. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  115. package/ai/token/prepareTokenUsageData.ts +88 -0
  116. package/ai/token/query.ts +88 -0
  117. package/ai/token/queryUserTokens.ts +59 -0
  118. package/ai/token/resolveBillingTarget.ts +52 -0
  119. package/ai/token/saveTokenRecord.ts +53 -0
  120. package/ai/token/serverDialogProjection.ts +78 -0
  121. package/ai/token/serverTokenWriter.ts +143 -0
  122. package/ai/token/stats.ts +21 -0
  123. package/ai/token/tokenThunks.ts +24 -0
  124. package/ai/token/types.ts +93 -0
  125. package/ai/tools/agent/agentTools.ts +176 -0
  126. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  127. package/ai/tools/agent/callAgentTool.ts +139 -0
  128. package/ai/tools/agent/createAgentTool.ts +512 -0
  129. package/ai/tools/agent/createDialogTool.ts +69 -0
  130. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  131. package/ai/tools/agent/parallelBudget.ts +221 -0
  132. package/ai/tools/agent/presets/appBuilderPreset.ts +145 -0
  133. package/ai/tools/agent/runLlmTool.ts +96 -0
  134. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  135. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  136. package/ai/tools/agent/skillAgentPreset.ts +89 -0
  137. package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
  138. package/ai/tools/agent/updateAgentTool.ts +96 -0
  139. package/ai/tools/agent/updateSelfTool.ts +113 -0
  140. package/ai/tools/amazonProductScraperTool.ts +86 -0
  141. package/ai/tools/apifyActorClient.ts +45 -0
  142. package/ai/tools/appEditGuard.ts +372 -0
  143. package/ai/tools/appReadSnapshot.ts +153 -0
  144. package/ai/tools/appTools.ts +1549 -0
  145. package/ai/tools/applyEditTool.ts +256 -0
  146. package/ai/tools/applyLineEditsTool.ts +312 -0
  147. package/ai/tools/browserTools/click.ts +33 -0
  148. package/ai/tools/browserTools/closeSession.ts +29 -0
  149. package/ai/tools/browserTools/common.ts +27 -0
  150. package/ai/tools/browserTools/openSession.ts +48 -0
  151. package/ai/tools/browserTools/readContent.ts +38 -0
  152. package/ai/tools/browserTools/selectOption.ts +46 -0
  153. package/ai/tools/browserTools/typeText.ts +42 -0
  154. package/ai/tools/category/createCategoryTool.ts +66 -0
  155. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  156. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  157. package/ai/tools/cfBrowserTools.ts +319 -0
  158. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  159. package/ai/tools/checkEnvTool.ts +65 -0
  160. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  161. package/ai/tools/codeSearchTool.ts +111 -0
  162. package/ai/tools/codeTools.ts +101 -0
  163. package/ai/tools/createDocTool.ts +132 -0
  164. package/ai/tools/createPlanTool.ts +999 -0
  165. package/ai/tools/createSkillDocTool.ts +155 -0
  166. package/ai/tools/createWorkflowTool.ts +154 -0
  167. package/ai/tools/deepseekOcrTool.ts +34 -0
  168. package/ai/tools/delayTool.ts +31 -0
  169. package/ai/tools/deleteSpacesTool.ts +325 -0
  170. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  171. package/ai/tools/devReloadUtils.ts +29 -0
  172. package/ai/tools/dialogMessageSearch.ts +137 -0
  173. package/ai/tools/doctorSkillTool.ts +72 -0
  174. package/ai/tools/ecommerceScraperTool.ts +86 -0
  175. package/ai/tools/emailTools.ts +549 -0
  176. package/ai/tools/evalSkillTool.ts +92 -0
  177. package/ai/tools/exaSearchTool.ts +64 -0
  178. package/ai/tools/execBashTool.ts +379 -0
  179. package/ai/tools/executeSqlTool.ts +192 -0
  180. package/ai/tools/fetchWebpageSupport.ts +309 -0
  181. package/ai/tools/fetchWebpageTool.ts +84 -0
  182. package/ai/tools/geminiImagePreviewTool.ts +361 -0
  183. package/ai/tools/generateDocxTool.ts +215 -0
  184. package/ai/tools/googleSearchScraperTool.ts +106 -0
  185. package/ai/tools/importDataTool.ts +133 -0
  186. package/ai/tools/importSkillTool.ts +162 -0
  187. package/ai/tools/index.ts +1858 -0
  188. package/ai/tools/listFilesTool.ts +82 -0
  189. package/ai/tools/listUserSpacesTool.ts +113 -0
  190. package/ai/tools/modelUsageTools.ts +142 -0
  191. package/ai/tools/olmOcrTool.ts +34 -0
  192. package/ai/tools/openaiImageTool.ts +218 -0
  193. package/ai/tools/paddleOcrTool.ts +34 -0
  194. package/ai/tools/prepareTools.ts +23 -0
  195. package/ai/tools/readDocTool.ts +84 -0
  196. package/ai/tools/readFileTool.ts +211 -0
  197. package/ai/tools/readTool.ts +163 -0
  198. package/ai/tools/readXPostTool.ts +233 -0
  199. package/ai/tools/rememberMemoryTool.ts +84 -0
  200. package/ai/tools/remotionVideoTool.ts +151 -0
  201. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  202. package/ai/tools/searchRepoTool.ts +115 -0
  203. package/ai/tools/searchWorkspaceTool.ts +259 -0
  204. package/ai/tools/skillFollowup.ts +86 -0
  205. package/ai/tools/surfWeatherTool.ts +169 -0
  206. package/ai/tools/table/addTableRowTool.ts +217 -0
  207. package/ai/tools/table/createTableTool.ts +315 -0
  208. package/ai/tools/table/rowTools.ts +366 -0
  209. package/ai/tools/table/schemaTools.ts +244 -0
  210. package/ai/tools/table/shareTableTool.ts +148 -0
  211. package/ai/tools/table/toolShared.ts +129 -0
  212. package/ai/tools/toolApiClient.ts +198 -0
  213. package/ai/tools/toolNameAliases.ts +57 -0
  214. package/ai/tools/toolResultError.ts +42 -0
  215. package/ai/tools/toolRunSlice.ts +303 -0
  216. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  217. package/ai/tools/toolVisibility.ts +4 -0
  218. package/ai/tools/types.ts +20 -0
  219. package/ai/tools/uiAskChoiceTool.ts +104 -0
  220. package/ai/tools/updateContentTitleTool.ts +84 -0
  221. package/ai/tools/updateDocTool.ts +105 -0
  222. package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
  223. package/ai/tools/whisperTool.ts +77 -0
  224. package/ai/tools/writeFileTool.ts +210 -0
  225. package/ai/tools/youtubeScraperTool.ts +116 -0
  226. package/ai/tools/ziweiChartTool.ts +678 -0
  227. package/ai/types.ts +55 -0
  228. package/ai/workflow/workflowExecutor.ts +323 -0
  229. package/ai/workflow/workflowSlice.ts +73 -0
  230. package/ai/workflow/workflowTypes.ts +106 -0
  231. package/client/compactDialog.ts +222 -0
  232. package/connector-experimental/capabilities.ts +73 -0
  233. package/connector-experimental/codexBinary.ts +41 -0
  234. package/connector-experimental/heartbeatLoop.ts +22 -0
  235. package/connector-experimental/index.ts +5 -0
  236. package/connector-experimental/machineInfo.ts +46 -0
  237. package/connector-experimental/protocol.ts +54 -0
  238. package/machineCommands.ts +4 -4
  239. package/package.json +8 -6
@@ -0,0 +1,76 @@
1
+ import { touchMemoryItems } from "./store";
2
+ import { buildMemoryOverlay } from "./overlay";
3
+ import { rankMemoryCandidates } from "./rank";
4
+ import { buildDefaultSubjects, chooseMemoryOwners, loadMemoryCandidates } from "./query";
5
+ import type { MemoryRuntimeResolution } from "./types";
6
+
7
+ const normalizeSelectedContent = (text: string): string =>
8
+ text
9
+ .trim()
10
+ .replace(/^(你要记住|请记住|以后记住|记住)[,,。.\s::]*/u, "")
11
+ .replace(/[。!?!?]+$/u, "")
12
+ .trim();
13
+
14
+ const selectRuntimeMemoryItems = (items: ReturnType<typeof rankMemoryCandidates>) => {
15
+ const seen = new Set<string>();
16
+ const unique = items.filter((item) => {
17
+ const key = `${item.kind}:${normalizeSelectedContent(item.content).toLowerCase()}`;
18
+ if (seen.has(key)) return false;
19
+ seen.add(key);
20
+ return true;
21
+ });
22
+ const selected: typeof unique = [];
23
+ const selectedIds = new Set<string>();
24
+ const add = (item: typeof unique[number] | undefined) => {
25
+ if (!item || selectedIds.has(item.id) || selected.length >= 4) return;
26
+ selected.push(item);
27
+ selectedIds.add(item.id);
28
+ };
29
+
30
+ add(unique.find((item) => item.ownerType === "user"));
31
+ add(unique.find((item) => item.ownerType === "space"));
32
+ add(unique.find((item) => item.kind === "procedural"));
33
+ for (const item of unique) {
34
+ add(item);
35
+ }
36
+ return selected;
37
+ };
38
+
39
+ export const resolveMemoryRuntime = async (input: {
40
+ userId?: string | null;
41
+ spaceId?: string | null;
42
+ agentKey: string;
43
+ userInput: string;
44
+ }): Promise<MemoryRuntimeResolution> => {
45
+ const owners = chooseMemoryOwners({
46
+ userId: input.userId,
47
+ spaceId: input.spaceId,
48
+ });
49
+ if (owners.length === 0) {
50
+ return { selectedItems: [], promptBlock: null };
51
+ }
52
+
53
+ const candidates = await loadMemoryCandidates({
54
+ owners,
55
+ subjects: buildDefaultSubjects({
56
+ userId: input.userId,
57
+ spaceId: input.spaceId,
58
+ agentKey: input.agentKey,
59
+ }),
60
+ kinds: ["episodic", "semantic", "procedural"],
61
+ ownerLimit: 20,
62
+ });
63
+
64
+ const ranked = selectRuntimeMemoryItems(
65
+ rankMemoryCandidates(candidates, input.userInput)
66
+ );
67
+ if (ranked.length === 0) {
68
+ return { selectedItems: [], promptBlock: null };
69
+ }
70
+
71
+ await touchMemoryItems(ranked);
72
+ return {
73
+ selectedItems: ranked,
74
+ promptBlock: buildMemoryOverlay(ranked),
75
+ };
76
+ };
@@ -0,0 +1,20 @@
1
+ import serverDb from "database/server/db";
2
+ import type { MemoryItem } from "./types";
3
+ import {
4
+ createMemoryItem,
5
+ touchMemoryItemsInDb,
6
+ writeMemoryItemWithIndexesToDb,
7
+ } from "./storeShared";
8
+
9
+ export { createMemoryItem, writeMemoryItemWithIndexesToDb, touchMemoryItemsInDb };
10
+
11
+ export const writeMemoryItemWithIndexes = async (item: MemoryItem): Promise<void> => {
12
+ return writeMemoryItemWithIndexesToDb(serverDb, item);
13
+ };
14
+
15
+ export const touchMemoryItems = async (
16
+ items: MemoryItem[],
17
+ now = new Date().toISOString()
18
+ ): Promise<void> => {
19
+ return touchMemoryItemsInDb(serverDb, items, now);
20
+ };
@@ -0,0 +1,76 @@
1
+ import { ulid } from "database/utils/ulid";
2
+ import {
3
+ createMemoryKey,
4
+ createMemoryOwnerIndexKey,
5
+ createMemorySubjectKindIndexKey,
6
+ } from "database/keys";
7
+ import type { MemoryItem } from "./types";
8
+
9
+ export const createMemoryItem = (
10
+ input: Omit<MemoryItem, "id" | "createdAt" | "lastActivatedAt" | "activationCount">
11
+ ): MemoryItem => {
12
+ const now = new Date().toISOString();
13
+ return {
14
+ id: ulid(),
15
+ createdAt: now,
16
+ lastActivatedAt: now,
17
+ activationCount: 0,
18
+ ...input,
19
+ };
20
+ };
21
+
22
+ export const writeMemoryItemWithIndexesToDb = async (
23
+ db: any,
24
+ item: MemoryItem
25
+ ): Promise<void> => {
26
+ const batch = db.batch();
27
+ batch.put(createMemoryKey(item.ownerType, item.ownerId, item.id), item);
28
+ batch.put(
29
+ createMemoryOwnerIndexKey(
30
+ item.ownerType,
31
+ item.ownerId,
32
+ item.createdAt,
33
+ item.id
34
+ ),
35
+ {
36
+ memoryKey: createMemoryKey(item.ownerType, item.ownerId, item.id),
37
+ ownerType: item.ownerType,
38
+ ownerId: item.ownerId,
39
+ memoryId: item.id,
40
+ }
41
+ );
42
+ batch.put(
43
+ createMemorySubjectKindIndexKey(
44
+ item.subjectType,
45
+ item.subjectId,
46
+ item.kind,
47
+ item.createdAt,
48
+ item.id
49
+ ),
50
+ {
51
+ memoryKey: createMemoryKey(item.ownerType, item.ownerId, item.id),
52
+ subjectType: item.subjectType,
53
+ subjectId: item.subjectId,
54
+ kind: item.kind,
55
+ memoryId: item.id,
56
+ }
57
+ );
58
+ await batch.write();
59
+ };
60
+
61
+ export const touchMemoryItemsInDb = async (
62
+ db: any,
63
+ items: MemoryItem[],
64
+ now = new Date().toISOString()
65
+ ): Promise<void> => {
66
+ if (items.length === 0) return;
67
+ const batch = db.batch();
68
+ for (const item of items) {
69
+ batch.put(createMemoryKey(item.ownerType, item.ownerId, item.id), {
70
+ ...item,
71
+ lastActivatedAt: now,
72
+ activationCount: (item.activationCount ?? 0) + 1,
73
+ });
74
+ }
75
+ await batch.write();
76
+ };
@@ -0,0 +1,46 @@
1
+ export type MemoryOwnerType = "user" | "space" | "system";
2
+ export type MemoryVisibility = "private" | "shared" | "public";
3
+ export type MemorySubjectType = "user" | "agent" | "space" | "project" | "system";
4
+ export type MemoryKind = "episodic" | "semantic" | "procedural";
5
+ export type MemoryFacet =
6
+ | "preference"
7
+ | "tension"
8
+ | "unfinished"
9
+ | "goal"
10
+ | "style";
11
+
12
+ export interface MemoryItem {
13
+ id: string;
14
+ ownerType: MemoryOwnerType;
15
+ ownerId: string;
16
+ visibility: MemoryVisibility;
17
+ subjectType: MemorySubjectType;
18
+ subjectId: string;
19
+ kind: MemoryKind;
20
+ content: string;
21
+ createdAt: string;
22
+ lastActivatedAt: string;
23
+ activationCount: number;
24
+ importance: number;
25
+ confidence: number;
26
+ tags?: string[];
27
+ facet?: MemoryFacet;
28
+ patternKey?: string;
29
+ sourceDialogId?: string;
30
+ sourceMessageId?: string;
31
+ }
32
+
33
+ export interface MemoryOwnerRef {
34
+ ownerType: MemoryOwnerType;
35
+ ownerId: string;
36
+ }
37
+
38
+ export interface MemorySubjectRef {
39
+ subjectType: MemorySubjectType;
40
+ subjectId: string;
41
+ }
42
+
43
+ export interface MemoryRuntimeResolution {
44
+ selectedItems: MemoryItem[];
45
+ promptBlock: string | null;
46
+ }
@@ -0,0 +1,349 @@
1
+ import type { ChatMessage } from "server/handlers/agentRun/types";
2
+ import { createMemoryItem, writeMemoryItemWithIndexesToDb } from "./storeShared";
3
+ import { loadMemoryCandidatesFromDb } from "./queryShared";
4
+ import type {
5
+ MemoryFacet,
6
+ MemoryItem,
7
+ MemoryOwnerRef,
8
+ MemoryVisibility,
9
+ } from "./types";
10
+
11
+ export interface UnderstandingMemoryCandidate {
12
+ facet: MemoryFacet;
13
+ content: string;
14
+ importance: number;
15
+ confidence: number;
16
+ patternKey: string;
17
+ tags: string[];
18
+ }
19
+
20
+ const UNDERSTANDING_TAG = "understanding-memory";
21
+
22
+ const normalizeClause = (value: string): string =>
23
+ value
24
+ .replace(/[“”"]/g, "")
25
+ .replace(/\s+/g, " ")
26
+ .replace(/[。!?!?;;]+$/u, "")
27
+ .trim();
28
+
29
+ const normalizeSignalLead = (value: string): string =>
30
+ normalizeClause(value.split(/[\n,,。!?!?;;]+/u)[0] ?? "");
31
+
32
+ const stripLead = (value: string): string =>
33
+ value
34
+ .replace(/^(明白|好的|对|是的|所以|其实|我记得|欢迎回来|嗯|那)\s*[,,::\-]?\s*/u, "")
35
+ .trim();
36
+
37
+ const contentToText = (content: ChatMessage["content"]): string => {
38
+ if (typeof content === "string") return content.trim();
39
+ if (!Array.isArray(content)) return "";
40
+ return content
41
+ .map((part) => (part?.type === "text" ? part.text.trim() : ""))
42
+ .filter(Boolean)
43
+ .join("\n")
44
+ .trim();
45
+ };
46
+
47
+ const extractPreference = (text: string): string[] => {
48
+ const matches: string[] = [];
49
+ const normalized = stripLead(text);
50
+
51
+ const moreValue = normalized.match(/[你我](?:更)?在意(.+)/u);
52
+ if (moreValue?.[1]) {
53
+ matches.push(`更在意${normalizeSignalLead(moreValue[1])}`);
54
+ }
55
+
56
+ const concern = normalized.match(/[你我](?:更)?关心(.+)/u);
57
+ if (concern?.[1]) {
58
+ matches.push(`更关心${normalizeSignalLead(concern[1])}`);
59
+ }
60
+
61
+ const fear = normalized.match(/[你我](?:更)?怕(.+)/u);
62
+ if (fear?.[1]) {
63
+ matches.push(`更怕${normalizeSignalLead(fear[1])}`);
64
+ }
65
+
66
+ const dontWant = normalized.match(/[你我]不想(.+)/u);
67
+ if (dontWant?.[1]) {
68
+ matches.push(`不想${normalizeSignalLead(dontWant[1])}`);
69
+ }
70
+
71
+ const dontHope = normalized.match(/[你我]不希望(.+)/u);
72
+ if (dontHope?.[1]) {
73
+ matches.push(`不希望${normalizeSignalLead(dontHope[1])}`);
74
+ }
75
+
76
+ return matches;
77
+ };
78
+
79
+ const extractStyle = (text: string): string[] => {
80
+ const matches: string[] = [];
81
+ const normalized = stripLead(text);
82
+
83
+ const dislike = normalized.match(/[你我]不喜欢(.+)/u);
84
+ if (dislike?.[1]) {
85
+ matches.push(`不喜欢${normalizeSignalLead(dislike[1])}`);
86
+ }
87
+
88
+ const prefer = normalized.match(/[你我](?:更)?喜欢(.+)/u);
89
+ if (prefer?.[1]) {
90
+ matches.push(`更喜欢${normalizeSignalLead(prefer[1])}`);
91
+ }
92
+
93
+ return matches;
94
+ };
95
+
96
+ const extractGoal = (text: string): string[] => {
97
+ const matches: string[] = [];
98
+ const normalized = stripLead(text);
99
+ if (/[??]/u.test(normalized)) {
100
+ return matches;
101
+ }
102
+
103
+ const wantFirst = normalized.match(/[你我](?:这次)?(?:更)?想先(?:把)?(.+)/u);
104
+ if (wantFirst?.[1]) {
105
+ matches.push(`想先${normalizeSignalLead(wantFirst[1])}`);
106
+ }
107
+
108
+ const shouldFirst = normalized.match(/[你我](?:还是)?想要先(?:把)?(.+)/u);
109
+ if (shouldFirst?.[1]) {
110
+ matches.push(`想先${normalizeSignalLead(shouldFirst[1])}`);
111
+ }
112
+
113
+ return matches;
114
+ };
115
+
116
+ const normalizeTensionTail = (tail: string): string =>
117
+ normalizeClause(
118
+ (
119
+ tail.replace(/^在/u, "").replace(/^权衡/u, "").replace(/^比较/u, "")
120
+ .split(/[。!?!?;;\n]/u)[0] ?? ""
121
+ )
122
+ );
123
+
124
+ const extractTension = (text: string): string[] => {
125
+ const matches: string[] = [];
126
+ const normalized = stripLead(text);
127
+
128
+ const weigh = normalized.match(/[你我]在权衡(.+)/u);
129
+ if (weigh?.[1]) {
130
+ matches.push(`在权衡${normalizeTensionTail(weigh[1])}`);
131
+ }
132
+
133
+ const struggle = normalized.match(/[你我](?:现在)?(?:真正)?纠结(?:的是)?[::,,\s]*(.+)/u);
134
+ if (struggle?.[1] && struggle[1].includes("还是")) {
135
+ matches.push(`在权衡${normalizeTensionTail(struggle[1])}`);
136
+ }
137
+
138
+ const stuck = normalized.match(/[你我]卡的点不是.+?而是(.+)/u);
139
+ if (stuck?.[1]) {
140
+ matches.push(`在权衡${normalizeTensionTail(stuck[1])}`);
141
+ }
142
+
143
+ return matches;
144
+ };
145
+
146
+ const extractUnfinished = (text: string): string[] => {
147
+ const matches: string[] = [];
148
+ const normalized = stripLead(text);
149
+
150
+ const direct = normalized.match(/[你我](?:还)?(?:没|尚未)决定(.+)/u);
151
+ if (direct?.[1]) {
152
+ matches.push(`还没决定${normalizeClause(direct[1])}`);
153
+ }
154
+
155
+ const uncertain = normalized.match(/[你我](?:还)?不确定(.+)/u);
156
+ if (uncertain?.[1]) {
157
+ matches.push(`还不确定${normalizeClause(uncertain[1])}`);
158
+ }
159
+
160
+ const notReady = normalized.match(/[你我](?:还)?没想好(.+)/u);
161
+ if (notReady?.[1]) {
162
+ matches.push(`还没想好${normalizeClause(notReady[1])}`);
163
+ }
164
+
165
+ return matches;
166
+ };
167
+
168
+ const buildPatternKey = (facet: MemoryFacet, content: string): string =>
169
+ `understanding:${facet}:${content.toLowerCase()}`;
170
+
171
+ const buildCandidate = (
172
+ facet: MemoryFacet,
173
+ content: string
174
+ ): UnderstandingMemoryCandidate | null => {
175
+ const normalized = normalizeClause(content);
176
+ if (!normalized || normalized.length < 6) return null;
177
+
178
+ const importance =
179
+ facet === "unfinished" ? 0.91 :
180
+ facet === "tension" ? 0.89 :
181
+ facet === "preference" ? 0.84 :
182
+ facet === "style" ? 0.8 :
183
+ 0.82;
184
+ const confidence =
185
+ facet === "unfinished" || facet === "tension" ? 0.76 : 0.72;
186
+
187
+ return {
188
+ facet,
189
+ content: normalized,
190
+ importance,
191
+ confidence,
192
+ patternKey: buildPatternKey(facet, normalized),
193
+ tags: [UNDERSTANDING_TAG, `memory-facet:${facet}`],
194
+ };
195
+ };
196
+
197
+ export const extractUnderstandingMemoryCandidates = (input: {
198
+ userInput: string;
199
+ trace?: ChatMessage[];
200
+ }): UnderstandingMemoryCandidate[] => {
201
+ const texts = [
202
+ input.userInput,
203
+ ...(input.trace ?? [])
204
+ .filter((message) => message.role === "assistant" || message.role === "user")
205
+ .map((message) => contentToText(message.content)),
206
+ ]
207
+ .map((value) => value.trim())
208
+ .filter(Boolean);
209
+
210
+ const candidates: UnderstandingMemoryCandidate[] = [];
211
+ for (const text of texts) {
212
+ for (const content of extractPreference(text)) {
213
+ const candidate = buildCandidate("preference", content);
214
+ if (candidate) candidates.push(candidate);
215
+ }
216
+ for (const content of extractStyle(text)) {
217
+ const candidate = buildCandidate("style", content);
218
+ if (candidate) candidates.push(candidate);
219
+ }
220
+ for (const content of extractGoal(text)) {
221
+ const candidate = buildCandidate("goal", content);
222
+ if (candidate) candidates.push(candidate);
223
+ }
224
+ for (const content of extractTension(text)) {
225
+ const candidate = buildCandidate("tension", content);
226
+ if (candidate) candidates.push(candidate);
227
+ }
228
+ for (const content of extractUnfinished(text)) {
229
+ const candidate = buildCandidate("unfinished", content);
230
+ if (candidate) candidates.push(candidate);
231
+ }
232
+ }
233
+
234
+ const seen = new Set<string>();
235
+ return candidates.filter((candidate) => {
236
+ const key = `${candidate.facet}:${candidate.content.toLowerCase()}`;
237
+ if (seen.has(key)) return false;
238
+ seen.add(key);
239
+ return true;
240
+ });
241
+ };
242
+
243
+ const buildUnderstandingTarget = (input: {
244
+ userId?: string | null;
245
+ spaceId?: string | null;
246
+ agentKey: string;
247
+ }): {
248
+ owner: MemoryOwnerRef;
249
+ visibility: MemoryVisibility;
250
+ } | null => {
251
+ if (input.userId) {
252
+ return {
253
+ owner: { ownerType: "user", ownerId: input.userId },
254
+ visibility: "private",
255
+ };
256
+ }
257
+ if (input.spaceId) {
258
+ return {
259
+ owner: { ownerType: "space", ownerId: input.spaceId },
260
+ visibility: "shared",
261
+ };
262
+ }
263
+ return null;
264
+ };
265
+
266
+ const sameUnderstanding = (item: MemoryItem, candidate: UnderstandingMemoryCandidate, agentKey: string) =>
267
+ item.subjectType === "agent" &&
268
+ item.subjectId === agentKey &&
269
+ item.patternKey === candidate.patternKey;
270
+
271
+ export const captureUnderstandingMemoryFromDialog = async (input: {
272
+ db: any;
273
+ userId?: string | null;
274
+ spaceId?: string | null;
275
+ agentKey: string;
276
+ dialogId: string;
277
+ userInput: string;
278
+ trace?: ChatMessage[];
279
+ }): Promise<void> => {
280
+ const target = buildUnderstandingTarget(input);
281
+ if (!target) return;
282
+
283
+ const candidates = extractUnderstandingMemoryCandidates({
284
+ userInput: input.userInput,
285
+ trace: input.trace,
286
+ });
287
+ if (candidates.length === 0) return;
288
+
289
+ const existing = await loadMemoryCandidatesFromDb(input.db, {
290
+ owners: [target.owner],
291
+ subjects: [{ subjectType: "agent", subjectId: input.agentKey }],
292
+ kinds: ["episodic", "semantic"],
293
+ ownerLimit: 100,
294
+ });
295
+
296
+ for (const candidate of candidates) {
297
+ const sameItems = existing.filter((item) => sameUnderstanding(item, candidate, input.agentKey));
298
+ const existingSemantic = sameItems.find((item) => item.kind === "semantic");
299
+ if (existingSemantic) continue;
300
+
301
+ const existingEpisode = sameItems.find((item) => item.kind === "episodic");
302
+ if (
303
+ existingEpisode &&
304
+ existingEpisode.sourceDialogId &&
305
+ existingEpisode.sourceDialogId !== input.dialogId
306
+ ) {
307
+ const semantic = createMemoryItem({
308
+ ownerType: target.owner.ownerType,
309
+ ownerId: target.owner.ownerId,
310
+ visibility: target.visibility,
311
+ subjectType: "agent",
312
+ subjectId: input.agentKey,
313
+ kind: "semantic",
314
+ content: candidate.content,
315
+ facet: candidate.facet,
316
+ importance: Math.min(0.95, candidate.importance + 0.03),
317
+ confidence: Math.min(0.86, candidate.confidence + 0.06),
318
+ tags: [...candidate.tags, "consolidated-understanding"],
319
+ patternKey: candidate.patternKey,
320
+ sourceDialogId: input.dialogId,
321
+ });
322
+ await writeMemoryItemWithIndexesToDb(input.db, semantic);
323
+ existing.push(semantic);
324
+ continue;
325
+ }
326
+
327
+ if (existingEpisode && existingEpisode.sourceDialogId === input.dialogId) {
328
+ continue;
329
+ }
330
+
331
+ const episodic = createMemoryItem({
332
+ ownerType: target.owner.ownerType,
333
+ ownerId: target.owner.ownerId,
334
+ visibility: target.visibility,
335
+ subjectType: "agent",
336
+ subjectId: input.agentKey,
337
+ kind: "episodic",
338
+ content: candidate.content,
339
+ facet: candidate.facet,
340
+ importance: candidate.importance,
341
+ confidence: candidate.confidence,
342
+ tags: candidate.tags,
343
+ patternKey: candidate.patternKey,
344
+ sourceDialogId: input.dialogId,
345
+ });
346
+ await writeMemoryItemWithIndexesToDb(input.db, episodic);
347
+ existing.push(episodic);
348
+ }
349
+ };