nolo-cli 0.1.12 → 0.1.14

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 (323) hide show
  1. package/README.md +54 -2
  2. package/agent-runtime/hostAdapter.ts +53 -0
  3. package/agent-runtime/index.ts +28 -0
  4. package/agent-runtime/localLoop.ts +62 -0
  5. package/agent-runtime/runtimeDecision.ts +70 -0
  6. package/agent-runtime/types.ts +87 -0
  7. package/agentRuntimeCommands.ts +141 -22
  8. package/agentRuntimeLocal.ts +7 -0
  9. package/ai/agent/_executeModel.ts +118 -0
  10. package/ai/agent/agentSlice.ts +545 -0
  11. package/ai/agent/appWorkingMemory.ts +126 -0
  12. package/ai/agent/avatarUtils.ts +24 -0
  13. package/ai/agent/buildEditingContext.ts +373 -0
  14. package/ai/agent/buildSystemPrompt.ts +532 -0
  15. package/ai/agent/cleanAgentMessages.ts +140 -0
  16. package/ai/agent/cliChatClient.ts +119 -0
  17. package/ai/agent/contextCompiler.ts +107 -0
  18. package/ai/agent/contextLayerContract.ts +44 -0
  19. package/ai/agent/createAgentSchema.ts +234 -0
  20. package/ai/agent/executeToolCall.ts +58 -0
  21. package/ai/agent/fetchAgentContexts.ts +42 -0
  22. package/ai/agent/generatePrompt.ts +3 -0
  23. package/ai/agent/getFullChatContextKeys.ts +168 -0
  24. package/ai/agent/hooks/fetchPublicAgents.ts +133 -0
  25. package/ai/agent/hooks/useAgentConfig.ts +61 -0
  26. package/ai/agent/hooks/useAgentDialog.ts +35 -0
  27. package/ai/agent/hooks/useAgentFormValidation.ts +202 -0
  28. package/ai/agent/hooks/usePublicAgents.ts +473 -0
  29. package/ai/agent/persistMessageWithFixedId.ts +37 -0
  30. package/ai/agent/planSlice.ts +259 -0
  31. package/ai/agent/referenceUtils.ts +229 -0
  32. package/ai/agent/runAgentBackground.ts +238 -0
  33. package/ai/agent/runAgentClientLoop.ts +138 -0
  34. package/ai/agent/runtimeGuidance.ts +97 -0
  35. package/ai/agent/runtimeServerBase.ts +37 -0
  36. package/ai/agent/server/fetchPublicAgents.ts +128 -0
  37. package/ai/agent/startParallelAgentStreams.ts +424 -0
  38. package/ai/agent/startupProtocol.ts +53 -0
  39. package/ai/agent/streamAgentChatTurn.ts +1299 -0
  40. package/ai/agent/streamAgentChatTurnUtils.ts +738 -0
  41. package/ai/agent/types.ts +71 -0
  42. package/ai/agent/utils/imageOutput.ts +39 -0
  43. package/ai/agent/utils/publicImageAgentMode.ts +26 -0
  44. package/ai/agent/utils/sortUtils.ts +250 -0
  45. package/ai/agent/web/referencePickerUtils.ts +146 -0
  46. package/ai/agent.ts +2 -0
  47. package/ai/ai.locale.ts +1083 -0
  48. package/ai/chat/accumulateToolCallChunks.ts +95 -0
  49. package/ai/chat/fetchUtils.native.ts +276 -0
  50. package/ai/chat/fetchUtils.ts +153 -0
  51. package/ai/chat/inlineImageUrlsForCustomProvider.ts +117 -0
  52. package/ai/chat/parseApiError.ts +64 -0
  53. package/ai/chat/parseMultilineSSE.ts +95 -0
  54. package/ai/chat/sendOpenAICompletionsRequest.native.ts +682 -0
  55. package/ai/chat/sendOpenAICompletionsRequest.ts +712 -0
  56. package/ai/chat/sendOpenAIResponseRequest.ts +512 -0
  57. package/ai/chat/shouldUseServerProxy.ts +18 -0
  58. package/ai/chat/sseClient.native.ts +91 -0
  59. package/ai/chat/sseClient.ts +67 -0
  60. package/ai/chat/streamReader.native.ts +31 -0
  61. package/ai/chat/streamReader.ts +62 -0
  62. package/ai/chat/updateTotalUsage.ts +72 -0
  63. package/ai/context/buildReferenceContext.ts +437 -0
  64. package/ai/context/calculateContextUsage.ts +133 -0
  65. package/ai/context/retention.ts +165 -0
  66. package/ai/context/tokenUtils.ts +78 -0
  67. package/ai/index.ts +1 -0
  68. package/ai/llm/agentCapabilities.ts +74 -0
  69. package/ai/llm/calculateGeminiImageTokens.ts +57 -0
  70. package/ai/llm/deepinfra.ts +28 -0
  71. package/ai/llm/fireworks.ts +68 -0
  72. package/ai/llm/generateRequestBody.ts +165 -0
  73. package/ai/llm/getModelContextWindow.ts +84 -0
  74. package/ai/llm/getNoloKey.ts +37 -0
  75. package/ai/llm/getPricing.ts +232 -0
  76. package/ai/llm/hooks/useModelPricing.ts +75 -0
  77. package/ai/llm/imagePricing.ts +66 -0
  78. package/ai/llm/isResponseAPIModel.ts +13 -0
  79. package/ai/llm/kimi.ts +18 -0
  80. package/ai/llm/mimo.ts +71 -0
  81. package/ai/llm/mistral.ts +22 -0
  82. package/ai/llm/modelAvatar.ts +427 -0
  83. package/ai/llm/models.ts +45 -0
  84. package/ai/llm/openrouterModels.ts +141 -0
  85. package/ai/llm/providers.ts +307 -0
  86. package/ai/llm/reasoningModels.ts +28 -0
  87. package/ai/llm/types.ts +59 -0
  88. package/ai/llm/usageRequestOptions.ts +59 -0
  89. package/ai/memory/capture.ts +148 -0
  90. package/ai/memory/consolidate.ts +104 -0
  91. package/ai/memory/delete.ts +147 -0
  92. package/ai/memory/overlay.ts +84 -0
  93. package/ai/memory/query.ts +38 -0
  94. package/ai/memory/queryShared.ts +160 -0
  95. package/ai/memory/rank.ts +105 -0
  96. package/ai/memory/recentRelationshipRecap.ts +247 -0
  97. package/ai/memory/remember.ts +167 -0
  98. package/ai/memory/runtime.ts +76 -0
  99. package/ai/memory/store.ts +20 -0
  100. package/ai/memory/storeShared.ts +76 -0
  101. package/ai/memory/types.ts +46 -0
  102. package/ai/memory/understanding.ts +349 -0
  103. package/ai/memory/understandingGreeting.ts +264 -0
  104. package/ai/messages/type.ts +20 -0
  105. package/ai/policy/personalizationDialog.ts +333 -0
  106. package/ai/policy/runtimePolicy.ts +440 -0
  107. package/ai/policy/selfUpdateFields.ts +48 -0
  108. package/ai/policy/types.ts +64 -0
  109. package/ai/skills/referenceRuntime.ts +274 -0
  110. package/ai/skills/skillDiagnostics.ts +251 -0
  111. package/ai/skills/skillDocBuilder.ts +139 -0
  112. package/ai/skills/skillDocProtocol.ts +434 -0
  113. package/ai/skills/skillReferenceSummary.ts +63 -0
  114. package/ai/skills/skillSummaryMarker.ts +26 -0
  115. package/ai/token/calculatePrice.ts +546 -0
  116. package/ai/token/db.ts +98 -0
  117. package/ai/token/externalToolCost.ts +321 -0
  118. package/ai/token/hooks/useRecords.ts +65 -0
  119. package/ai/token/missingUsageEstimate.ts +42 -0
  120. package/ai/token/modelUsageQuery.ts +252 -0
  121. package/ai/token/normalizeUsage.ts +84 -0
  122. package/ai/token/openaiImageGenerationUsage.ts +56 -0
  123. package/ai/token/prepareTokenUsageData.ts +88 -0
  124. package/ai/token/query.ts +88 -0
  125. package/ai/token/queryUserTokens.ts +59 -0
  126. package/ai/token/resolveBillingTarget.ts +52 -0
  127. package/ai/token/saveTokenRecord.ts +53 -0
  128. package/ai/token/serverDialogProjection.ts +78 -0
  129. package/ai/token/serverTokenWriter.ts +143 -0
  130. package/ai/token/stats.ts +21 -0
  131. package/ai/token/tokenThunks.ts +24 -0
  132. package/ai/token/types.ts +93 -0
  133. package/ai/tools/agent/agentTools.ts +176 -0
  134. package/ai/tools/agent/agentUpdateShared.ts +311 -0
  135. package/ai/tools/agent/callAgentTool.ts +139 -0
  136. package/ai/tools/agent/createAgentTool.ts +512 -0
  137. package/ai/tools/agent/createDialogTool.ts +69 -0
  138. package/ai/tools/agent/createSkillAgentTool.ts +62 -0
  139. package/ai/tools/agent/parallelBudget.ts +221 -0
  140. package/ai/tools/agent/presets/appBuilderPreset.ts +147 -0
  141. package/ai/tools/agent/runLlmTool.ts +96 -0
  142. package/ai/tools/agent/runStreamingAgentTool.ts +73 -0
  143. package/ai/tools/agent/skillAgentArgs.ts +106 -0
  144. package/ai/tools/agent/skillAgentPreset.ts +89 -0
  145. package/ai/tools/agent/streamParallelAgentsTool.ts +122 -0
  146. package/ai/tools/agent/updateAgentTool.ts +96 -0
  147. package/ai/tools/agent/updateSelfTool.ts +113 -0
  148. package/ai/tools/amazonProductScraperTool.ts +86 -0
  149. package/ai/tools/apifyActorClient.ts +45 -0
  150. package/ai/tools/appEditGuard.ts +372 -0
  151. package/ai/tools/appReadSnapshot.ts +153 -0
  152. package/ai/tools/appTools.ts +1549 -0
  153. package/ai/tools/applyEditTool.ts +256 -0
  154. package/ai/tools/applyLineEditsTool.ts +312 -0
  155. package/ai/tools/browserTools/click.ts +33 -0
  156. package/ai/tools/browserTools/closeSession.ts +29 -0
  157. package/ai/tools/browserTools/common.ts +27 -0
  158. package/ai/tools/browserTools/openSession.ts +48 -0
  159. package/ai/tools/browserTools/readContent.ts +38 -0
  160. package/ai/tools/browserTools/selectOption.ts +46 -0
  161. package/ai/tools/browserTools/typeText.ts +42 -0
  162. package/ai/tools/category/createCategoryTool.ts +66 -0
  163. package/ai/tools/category/queryContentsByCategoryTool.ts +69 -0
  164. package/ai/tools/category/updateContentCategoryTool.ts +75 -0
  165. package/ai/tools/cfBrowserTools.ts +319 -0
  166. package/ai/tools/cfSpeechToTextTool.ts +49 -0
  167. package/ai/tools/checkEnvTool.ts +65 -0
  168. package/ai/tools/cloudflareCrawlTool.ts +289 -0
  169. package/ai/tools/codeSearchTool.ts +111 -0
  170. package/ai/tools/codeTools.ts +101 -0
  171. package/ai/tools/createDocTool.ts +132 -0
  172. package/ai/tools/createPlanTool.ts +999 -0
  173. package/ai/tools/createSkillDocTool.ts +155 -0
  174. package/ai/tools/createWorkflowTool.ts +154 -0
  175. package/ai/tools/deepseekOcrTool.ts +34 -0
  176. package/ai/tools/delayTool.ts +31 -0
  177. package/ai/tools/deleteSpacesTool.ts +325 -0
  178. package/ai/tools/deleteSpacesToolModel.ts +159 -0
  179. package/ai/tools/devReloadUtils.ts +29 -0
  180. package/ai/tools/dialogMessageSearch.ts +137 -0
  181. package/ai/tools/doctorSkillTool.ts +72 -0
  182. package/ai/tools/ecommerceScraperTool.ts +86 -0
  183. package/ai/tools/emailTools.ts +549 -0
  184. package/ai/tools/evalSkillTool.ts +92 -0
  185. package/ai/tools/exaSearchTool.ts +64 -0
  186. package/ai/tools/execBashTool.ts +379 -0
  187. package/ai/tools/executeSqlTool.ts +192 -0
  188. package/ai/tools/fetchWebpageSupport.ts +309 -0
  189. package/ai/tools/fetchWebpageTool.ts +84 -0
  190. package/ai/tools/geminiImagePreviewTool.ts +361 -0
  191. package/ai/tools/generateDocxTool.ts +215 -0
  192. package/ai/tools/googleSearchScraperTool.ts +106 -0
  193. package/ai/tools/importDataTool.ts +133 -0
  194. package/ai/tools/importSkillTool.ts +162 -0
  195. package/ai/tools/index.ts +1927 -0
  196. package/ai/tools/listFilesTool.ts +82 -0
  197. package/ai/tools/listUserSpacesTool.ts +113 -0
  198. package/ai/tools/modelUsageTools.ts +199 -0
  199. package/ai/tools/olmOcrTool.ts +34 -0
  200. package/ai/tools/openaiImageTool.ts +267 -0
  201. package/ai/tools/prepareTools.ts +23 -0
  202. package/ai/tools/readDocTool.ts +84 -0
  203. package/ai/tools/readFileTool.ts +211 -0
  204. package/ai/tools/readTool.ts +163 -0
  205. package/ai/tools/readXPostTool.ts +233 -0
  206. package/ai/tools/rememberMemoryTool.ts +84 -0
  207. package/ai/tools/remotionVideoTool.ts +151 -0
  208. package/ai/tools/searchDialogMessagesTool.ts +222 -0
  209. package/ai/tools/searchRepoTool.ts +115 -0
  210. package/ai/tools/searchWorkspaceTool.ts +259 -0
  211. package/ai/tools/skillFollowup.ts +86 -0
  212. package/ai/tools/surfWeatherTool.ts +169 -0
  213. package/ai/tools/table/addTableRowTool.ts +217 -0
  214. package/ai/tools/table/createTableTool.ts +315 -0
  215. package/ai/tools/table/rowTools.ts +366 -0
  216. package/ai/tools/table/schemaTools.ts +244 -0
  217. package/ai/tools/table/shareTableTool.ts +148 -0
  218. package/ai/tools/table/toolShared.ts +129 -0
  219. package/ai/tools/toolApiClient.ts +198 -0
  220. package/ai/tools/toolNameAliases.ts +57 -0
  221. package/ai/tools/toolResultError.ts +42 -0
  222. package/ai/tools/toolRunSlice.ts +303 -0
  223. package/ai/tools/toolSchemaCompatibility.ts +53 -0
  224. package/ai/tools/toolVisibility.ts +4 -0
  225. package/ai/tools/types.ts +20 -0
  226. package/ai/tools/uiAskChoiceTool.ts +104 -0
  227. package/ai/tools/updateContentTitleTool.ts +84 -0
  228. package/ai/tools/updateDocTool.ts +105 -0
  229. package/ai/tools/updateUserPreferenceProfileTool.ts +145 -0
  230. package/ai/tools/whisperTool.ts +77 -0
  231. package/ai/tools/writeFileTool.ts +210 -0
  232. package/ai/tools/youtubeScraperTool.ts +116 -0
  233. package/ai/tools/ziweiChartTool.ts +678 -0
  234. package/ai/types.ts +55 -0
  235. package/ai/workflow/workflowExecutor.ts +323 -0
  236. package/ai/workflow/workflowSlice.ts +73 -0
  237. package/ai/workflow/workflowTypes.ts +106 -0
  238. package/authCommands.ts +185 -21
  239. package/client/agentRun.test.ts +240 -0
  240. package/client/agentRun.ts +182 -19
  241. package/client/compactDialog.test.ts +238 -0
  242. package/client/compactDialog.ts +5 -2
  243. package/client/localRuntimeAdapter.test.ts +135 -0
  244. package/client/localRuntimeAdapter.ts +244 -0
  245. package/client/profileConfig.test.ts +40 -0
  246. package/client/streamingOutput.test.ts +22 -0
  247. package/client/streamingOutput.ts +38 -0
  248. package/commandRegistry.ts +11 -2
  249. package/connector-experimental/index.ts +5 -0
  250. package/database/actions/cacheMergedUserData.ts +64 -0
  251. package/database/actions/common.ts +242 -0
  252. package/database/actions/deleteFile.ts +40 -0
  253. package/database/actions/fetchUserData.ts +16 -0
  254. package/database/actions/fileContent.ts +125 -0
  255. package/database/actions/patch.ts +155 -0
  256. package/database/actions/read.ts +337 -0
  257. package/database/actions/readAndWait.ts +224 -0
  258. package/database/actions/readRequestManager.ts +120 -0
  259. package/database/actions/remove.ts +94 -0
  260. package/database/actions/replication.ts +366 -0
  261. package/database/actions/upload.ts +174 -0
  262. package/database/actions/upsert.ts +56 -0
  263. package/database/actions/write.ts +126 -0
  264. package/database/client/db.native.ts +73 -0
  265. package/database/client/db.ts +51 -0
  266. package/database/client/fetchUserData.ts +61 -0
  267. package/database/client/handleError.ts +19 -0
  268. package/database/client/queryRequest.ts +21 -0
  269. package/database/config.ts +21 -0
  270. package/database/dbActionThunks.ts +1 -0
  271. package/database/dbSlice.ts +149 -0
  272. package/database/email.ts +42 -0
  273. package/database/fileRing.ts +51 -0
  274. package/database/fileSharding.ts +70 -0
  275. package/database/fileStorage.native.ts +92 -0
  276. package/database/fileStorage.ts +232 -0
  277. package/database/fileUrl.ts +34 -0
  278. package/database/hooks/useUserData.ts +489 -0
  279. package/database/index.ts +1 -0
  280. package/database/keys.ts +765 -0
  281. package/database/queryPrefixes.ts +14 -0
  282. package/database/requests.ts +443 -0
  283. package/database/runtimeServerContext.ts +35 -0
  284. package/database/server/MemoryDB.ts +76 -0
  285. package/database/server/actorAccess.ts +76 -0
  286. package/database/server/agentDelegation.ts +124 -0
  287. package/database/server/coreDataOwnership.ts +13 -0
  288. package/database/server/coreDataProxy.ts +76 -0
  289. package/database/server/cybotReadonly.ts +18 -0
  290. package/database/server/dataHandlers.ts +111 -0
  291. package/database/server/db.ts +118 -0
  292. package/database/server/dbPath.ts +20 -0
  293. package/database/server/delete.ts +499 -0
  294. package/database/server/emailRepository.ts +1480 -0
  295. package/database/server/ensureDbOpen.ts +12 -0
  296. package/database/server/fileRead.ts +337 -0
  297. package/database/server/fileService.ts +436 -0
  298. package/database/server/handleTransaction.ts +86 -0
  299. package/database/server/patch.ts +282 -0
  300. package/database/server/query.ts +138 -0
  301. package/database/server/read.ts +325 -0
  302. package/database/server/resourceAccess.ts +211 -0
  303. package/database/server/routes.ts +110 -0
  304. package/database/server/spaceMemberAuthority.ts +67 -0
  305. package/database/server/upload.ts +159 -0
  306. package/database/server/write.ts +494 -0
  307. package/database/server/writeAuthority.ts +133 -0
  308. package/database/sqliteDb.ts +46 -0
  309. package/database/table/deleteTable.ts +120 -0
  310. package/database/tenantPlacement.ts +57 -0
  311. package/database/tombstones.ts +52 -0
  312. package/database/userDataLoadDecision.ts +17 -0
  313. package/database/userDataMerge.ts +95 -0
  314. package/database/userPreferenceRegister.ts +108 -0
  315. package/database/utils/dbPath.ts +47 -0
  316. package/database/utils/ulid.native.ts +6 -0
  317. package/database/utils/ulid.ts +1 -0
  318. package/index.ts +25 -15
  319. package/localRuntimeDb.ts +28 -0
  320. package/package.json +16 -4
  321. package/runtimeModeArgs.ts +33 -0
  322. package/tui/readlineWorkspace.ts +1 -0
  323. package/tui/session.ts +22 -0
@@ -0,0 +1,244 @@
1
+ import type {
2
+ AgentRuntimeAgentConfig,
3
+ AgentRuntimeHostAdapter,
4
+ AgentRuntimeSaveTurnInput,
5
+ } from "../agentRuntimeLocal";
6
+ import type { AgentRuntimeChatMessage } from "../../agent-runtime";
7
+ import { getDefaultCliLocalRuntimeDb } from "../localRuntimeDb";
8
+
9
+ type EnvLike = Record<string, string | undefined>;
10
+
11
+ export type CliLocalRuntimeDb = {
12
+ get(key: string): Promise<any>;
13
+ put(key: string, value: any): Promise<unknown>;
14
+ batch(ops: Array<{ type: "put"; key: string; value: any }>): Promise<unknown>;
15
+ iterator(options: { gte: string; lte?: string; lt?: string; reverse?: boolean; limit?: number }): AsyncIterable<[string, any]>;
16
+ };
17
+
18
+ export type CliLocalRuntimeAdapterDeps = {
19
+ env: EnvLike;
20
+ db?: CliLocalRuntimeDb;
21
+ now?: () => number;
22
+ createId?: () => string;
23
+ fetchImpl?: typeof fetch;
24
+ };
25
+
26
+ async function defaultLocalRuntimeDb(): Promise<CliLocalRuntimeDb> {
27
+ return getDefaultCliLocalRuntimeDb();
28
+ }
29
+
30
+ function createFallbackId() {
31
+ return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`.toUpperCase();
32
+ }
33
+
34
+ function resolveOpenAiCompatibleBaseUrl(env: EnvLike) {
35
+ return (env.NOLO_LOCAL_OPENAI_BASE_URL || env.OPENAI_BASE_URL || "https://api.openai.com/v1")
36
+ .replace(/\/+$/, "");
37
+ }
38
+
39
+ function resolveApiKey(env: EnvLike) {
40
+ return env.OPENAI_API_KEY || env.NOLO_LOCAL_OPENAI_API_KEY || "";
41
+ }
42
+
43
+ function resolveLocalUserId(env: EnvLike) {
44
+ return env.NOLO_LOCAL_USER_ID || env.NOLO_USER_ID || "local";
45
+ }
46
+
47
+ async function resolveDb(deps: CliLocalRuntimeAdapterDeps) {
48
+ return deps.db ?? defaultLocalRuntimeDb();
49
+ }
50
+
51
+ async function readAgentFromDb(args: {
52
+ db: CliLocalRuntimeDb;
53
+ agentRef: string;
54
+ userId: string;
55
+ }): Promise<AgentRuntimeAgentConfig | null> {
56
+ const candidates = [
57
+ args.agentRef,
58
+ `agent-${args.userId}-${args.agentRef}`,
59
+ `cybot-${args.userId}-${args.agentRef}`,
60
+ ];
61
+ for (const key of candidates) {
62
+ try {
63
+ const record = await args.db.get(key);
64
+ if (!record || typeof record !== "object") continue;
65
+ return {
66
+ key,
67
+ ...(typeof record.name === "string" ? { name: record.name } : {}),
68
+ ...(typeof record.prompt === "string" ? { prompt: record.prompt } : {}),
69
+ ...(typeof record.model === "string" ? { model: record.model } : {}),
70
+ ...(typeof record.provider === "string" ? { provider: record.provider } : {}),
71
+ ...(Array.isArray(record.toolNames) ? { toolNames: record.toolNames } : {}),
72
+ };
73
+ } catch {
74
+ // Try the next local LevelDB key convention.
75
+ }
76
+ }
77
+ return null;
78
+ }
79
+
80
+ function toOpenAiMessages(messages: AgentRuntimeChatMessage[]) {
81
+ return messages.map((message) => ({
82
+ role: message.role,
83
+ content: typeof message.content === "string"
84
+ ? message.content
85
+ : Array.isArray(message.content)
86
+ ? message.content
87
+ .map((part) => part.type === "text" ? part.text : `[image:${part.image_url.url}]`)
88
+ .join("\n")
89
+ : "",
90
+ }));
91
+ }
92
+
93
+ async function readDialogMessages(args: {
94
+ db: CliLocalRuntimeDb;
95
+ dialogId: string;
96
+ }) {
97
+ const messages: AgentRuntimeChatMessage[] = [];
98
+ const prefix = `dialog-${args.dialogId}-msg-`;
99
+ const iterator = args.db.iterator({ gte: prefix, lte: `${prefix}\uffff` });
100
+ for await (const [, value] of iterator) {
101
+ if (!value || typeof value !== "object") continue;
102
+ if (value.role === "system") continue;
103
+ if (value.role === "user" || value.role === "assistant" || value.role === "tool") {
104
+ messages.push({
105
+ role: value.role,
106
+ content: value.content ?? null,
107
+ ...(typeof value.toolCallId === "string" ? { tool_call_id: value.toolCallId } : {}),
108
+ ...(Array.isArray(value.tool_calls) ? { tool_calls: value.tool_calls } : {}),
109
+ });
110
+ }
111
+ }
112
+ return messages;
113
+ }
114
+
115
+ async function writeDialog(args: {
116
+ db: CliLocalRuntimeDb;
117
+ input: AgentRuntimeSaveTurnInput;
118
+ userId: string;
119
+ now: () => number;
120
+ createId: () => string;
121
+ }) {
122
+ const dialogId = args.createId();
123
+ const now = args.now();
124
+ const nowIso = new Date(now).toISOString();
125
+ const dialogKey = `dialog-${args.userId}-${dialogId}`;
126
+ const lastUser = [...args.input.messages].reverse().find((message) => message.role === "user");
127
+ const ops: Array<{ type: "put"; key: string; value: any }> = [
128
+ {
129
+ type: "put",
130
+ key: dialogKey,
131
+ value: {
132
+ id: dialogId,
133
+ dbKey: dialogKey,
134
+ type: "dialog",
135
+ userId: args.userId,
136
+ cybots: [args.input.agentKey],
137
+ primaryAgentKey: args.input.agentKey,
138
+ title: typeof lastUser?.content === "string" && lastUser.content.trim()
139
+ ? lastUser.content.trim().slice(0, 80)
140
+ : "Local agent run",
141
+ status: "done",
142
+ triggerType: "cli-local",
143
+ executionMode: "foreground",
144
+ createdAt: nowIso,
145
+ updatedAt: nowIso,
146
+ finishedAt: now,
147
+ usage: args.input.result.usage,
148
+ },
149
+ },
150
+ {
151
+ type: "put",
152
+ key: `dialog-${dialogId}-msg-user`,
153
+ value: {
154
+ id: "msg-user",
155
+ dbKey: `dialog-${dialogId}-msg-user`,
156
+ dialogId,
157
+ role: "user",
158
+ content: lastUser?.content ?? "",
159
+ userId: args.userId,
160
+ createdAt: nowIso,
161
+ },
162
+ },
163
+ {
164
+ type: "put",
165
+ key: `dialog-${dialogId}-msg-assistant`,
166
+ value: {
167
+ id: "msg-assistant",
168
+ dbKey: `dialog-${dialogId}-msg-assistant`,
169
+ dialogId,
170
+ role: "assistant",
171
+ content: args.input.result.content,
172
+ agentKey: args.input.agentKey,
173
+ cybotKey: args.input.agentKey,
174
+ createdAt: nowIso,
175
+ },
176
+ },
177
+ ];
178
+ await args.db.batch(ops);
179
+ return { dialogId };
180
+ }
181
+
182
+ export function createCliLocalRuntimeAdapter(
183
+ deps: CliLocalRuntimeAdapterDeps
184
+ ): AgentRuntimeHostAdapter {
185
+ const now = deps.now ?? Date.now;
186
+ const createId = deps.createId ?? createFallbackId;
187
+ const fetchImpl = deps.fetchImpl ?? fetch;
188
+ const userId = resolveLocalUserId(deps.env);
189
+
190
+ return {
191
+ host: "cli",
192
+ capabilities: ["leveldb-agent-config", "local-provider", "leveldb-persistence"],
193
+ loadAgentConfig: async (agentRef) => readAgentFromDb({
194
+ agentRef,
195
+ db: await resolveDb(deps),
196
+ userId,
197
+ }),
198
+ loadDialogHistory: async (dialogId) => readDialogMessages({
199
+ dialogId,
200
+ db: await resolveDb(deps),
201
+ }),
202
+ saveTurn: async (input) => writeDialog({
203
+ db: await resolveDb(deps),
204
+ input,
205
+ userId,
206
+ now,
207
+ createId,
208
+ }),
209
+ resolveProvider: async (agentConfig) => ({
210
+ model: agentConfig.model || "gpt-4.1-mini",
211
+ complete: async (messages) => {
212
+ const res = await fetchImpl(`${resolveOpenAiCompatibleBaseUrl(deps.env)}/chat/completions`, {
213
+ method: "POST",
214
+ headers: {
215
+ "Content-Type": "application/json",
216
+ ...(resolveApiKey(deps.env)
217
+ ? { Authorization: `Bearer ${resolveApiKey(deps.env)}` }
218
+ : {}),
219
+ },
220
+ body: JSON.stringify({
221
+ model: agentConfig.model || "gpt-4.1-mini",
222
+ messages: toOpenAiMessages(messages),
223
+ stream: false,
224
+ }),
225
+ });
226
+ const data = await res.json().catch(() => ({}));
227
+ if (!res.ok) {
228
+ throw new Error(`local provider failed: HTTP ${res.status} ${JSON.stringify(data)}`);
229
+ }
230
+ const content = String(data?.choices?.[0]?.message?.content ?? "");
231
+ return {
232
+ content,
233
+ model: agentConfig.model || "gpt-4.1-mini",
234
+ provider: agentConfig.provider || "openai-compatible",
235
+ usage: data?.usage,
236
+ trace: messages,
237
+ };
238
+ },
239
+ }),
240
+ executeTool: async () => {
241
+ throw new Error("Local runtime tools are not enabled for text-only CLI runs.");
242
+ },
243
+ };
244
+ }
@@ -0,0 +1,40 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+
6
+ import {
7
+ buildEnvFromProfile,
8
+ loadProfileConfig,
9
+ saveDefaultProfile,
10
+ } from "./profileConfig";
11
+
12
+ describe("cli profile config", () => {
13
+ test("saves and loads the default profile", () => {
14
+ const dir = mkdtempSync(join(tmpdir(), "nolo-profile-"));
15
+ try {
16
+ const path = join(dir, "config.json");
17
+
18
+ saveDefaultProfile(path, {
19
+ serverUrl: "https://nolo.chat",
20
+ authToken: "token-123",
21
+ agentKey: "agent-pub-abc",
22
+ agentName: "app-builder",
23
+ });
24
+
25
+ const config = loadProfileConfig(path);
26
+ expect(config.currentProfile).toBe("default");
27
+ expect(config.profiles.default.serverUrl).toBe("https://nolo.chat");
28
+ expect(config.profiles.default.authToken).toBe("token-123");
29
+ expect(buildEnvFromProfile(config)).toEqual({
30
+ NOLO_PROFILE: "default",
31
+ NOLO_SERVER: "https://nolo.chat",
32
+ AUTH_TOKEN: "token-123",
33
+ NOLO_AGENT: "agent-pub-abc",
34
+ NOLO_AGENT_NAME: "app-builder",
35
+ });
36
+ } finally {
37
+ rmSync(dir, { recursive: true, force: true });
38
+ }
39
+ });
40
+ });
@@ -0,0 +1,22 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { createStreamingTextWriter, splitStreamingText } from "./streamingOutput";
4
+
5
+ describe("streamingOutput", () => {
6
+ test("splits text into user-visible characters instead of UTF-16 code units", () => {
7
+ expect(splitStreamingText("A你👍🏽B")).toEqual(["A", "你", "👍🏽", "B"]);
8
+ });
9
+
10
+ test("flushes text in small character batches", () => {
11
+ const chunks: string[] = [];
12
+ const writer = createStreamingTextWriter({
13
+ write: (chunk) => chunks.push(chunk),
14
+ batchSize: 2,
15
+ });
16
+
17
+ writer.push("abcd");
18
+ writer.flushAll();
19
+
20
+ expect(chunks).toEqual(["ab", "cd"]);
21
+ });
22
+ });
@@ -0,0 +1,38 @@
1
+ type OutputLike = {
2
+ write(chunk: string): unknown;
3
+ };
4
+
5
+ export function splitStreamingText(content: string): string[] {
6
+ const Segmenter = Intl.Segmenter;
7
+ if (typeof Segmenter === "function") {
8
+ const segmenter = new Segmenter(undefined, { granularity: "grapheme" });
9
+ return Array.from(segmenter.segment(content), (part) => part.segment);
10
+ }
11
+ return Array.from(content);
12
+ }
13
+
14
+ export function createStreamingTextWriter({
15
+ write,
16
+ batchSize = 3,
17
+ }: {
18
+ write: OutputLike["write"];
19
+ batchSize?: number;
20
+ }) {
21
+ const queue: string[] = [];
22
+ const safeBatchSize = Math.max(1, batchSize);
23
+
24
+ const flushNext = () => {
25
+ if (!queue.length) return;
26
+ write(queue.splice(0, safeBatchSize).join(""));
27
+ };
28
+
29
+ return {
30
+ push(content: string) {
31
+ queue.push(...splitStreamingText(content));
32
+ flushNext();
33
+ },
34
+ flushAll() {
35
+ while (queue.length) flushNext();
36
+ },
37
+ };
38
+ }
@@ -16,6 +16,7 @@ export const COMMANDS: CommandEntry[] = [
16
16
  { path: ["skill-doc", "read"], script: "readSkillDoc.ts", description: "Read a skill doc" },
17
17
  { path: ["skill-doc", "update"], script: "updateSkillDoc.ts", description: "Update a skill doc" },
18
18
  { path: ["skill-doc", "delete"], script: "deleteSkillDoc.ts", description: "Delete a skill doc" },
19
+ { path: ["skill", "create-doc"], script: "createSkillDoc.ts", description: "Create a skill-backed doc" },
19
20
 
20
21
  { path: ["dialog", "read"], script: "readDialog.ts", description: "Read a dialog" },
21
22
  { path: ["space", "read"], script: "readSpace.ts", description: "Read a space" },
@@ -41,6 +42,8 @@ export const COMMANDS: CommandEntry[] = [
41
42
  { path: ["agent", "setup-demo"], script: "setupDemoAgent.ts", description: "Bootstrap demo publisher agents" },
42
43
  { path: ["agent", "supervise"], script: "runAutonomousAgent.ts", description: "Run an agent in autonomous cycles" },
43
44
 
45
+ { path: ["doctor", "runtime"], script: "", description: "Diagnose local-first agent runtime selection" },
46
+
44
47
  { path: ["llama"], script: "llamaServerSupervisor.ts", description: "Manage the llama.cpp runtime" },
45
48
  { path: ["model-runtime"], script: "localModelRuntimeSupervisor.ts", description: "Manage local model runtime processes" },
46
49
  { path: ["dev"], script: "devControl.ts", description: "Manage the local dev environment" },
@@ -50,7 +53,9 @@ export const GROUP_ORDER = [
50
53
  "chat",
51
54
  "doc",
52
55
  "skill-doc",
56
+ "skill",
53
57
  "agent",
58
+ "doctor",
54
59
  "dialog",
55
60
  "space",
56
61
  "table",
@@ -73,6 +78,8 @@ export function renderHelpText() {
73
78
  " nolo",
74
79
  " nolo chat",
75
80
  " nolo login",
81
+ " nolo login --no-browser",
82
+ " nolo login --token <auth-token>",
76
83
  " nolo whoami",
77
84
  " nolo connect",
78
85
  " nolo connect --watch",
@@ -81,15 +88,17 @@ export function renderHelpText() {
81
88
  " nolo connect --daemon --server-url https://api.nolo.chat --api-key sk_machine_xxx",
82
89
  " nolo machine status",
83
90
  " nolo doctor",
91
+ " nolo doctor runtime",
84
92
  " nolo update",
85
- ' nolo doc create --title "Trip Notes" --body "hello"',
86
- ' nolo skill-doc create --title "Agent Query Skill" --description "Inspect recent agent dialogs"',
93
+ ' nolo doc create --title "Trip Notes" --body "hello" --sync local,us --dry-run',
94
+ ' nolo skill-doc create --title "Agent Query Skill" --description "Inspect recent agent dialogs" --sync local,main,us',
87
95
  " nolo agent list --json",
88
96
  " nolo agent read agent-pub-01APPBUILDER00000001YAII3I",
89
97
  " nolo agent bind-current agent-user-1-agent-1",
90
98
  " nolo agent runtime-doctor agent-user-1-agent-1",
91
99
  ' nolo agent smoke-current agent-user-1-agent-1 --msg "ping"',
92
100
  ' nolo chat --agent agent-pub-01APPBUILDER00000001YAII3I --msg "你好"',
101
+ " nolo space read 01KKY77TT0DA9NY7TNW3R7255N --content-key page-user-id --brief",
93
102
  " nolo space delete --name-prefix rn_owner_verify_0504 --yes",
94
103
  " nolo table data --table 01ABCXYZ --action query",
95
104
  " nolo llama status",
@@ -0,0 +1,5 @@
1
+ export * from "./capabilities";
2
+ export * from "./codexBinary";
3
+ export * from "./heartbeatLoop";
4
+ export * from "./machineInfo";
5
+ export * from "./protocol";
@@ -0,0 +1,64 @@
1
+ import { createAsyncThunk } from "@reduxjs/toolkit";
2
+
3
+ import type { AppThunkApi } from "app/store";
4
+
5
+ import { fetchFromClientDb } from "./common";
6
+ import { shouldReplaceWithNextRecord } from "../tombstones";
7
+
8
+ type CacheableUserDataRecord = {
9
+ dbKey?: string;
10
+ serverOrigin?: string;
11
+ [key: string]: unknown;
12
+ };
13
+
14
+ export const shouldUpdateLocalUserDataCache = (
15
+ nextRecord: CacheableUserDataRecord,
16
+ localRecord: CacheableUserDataRecord | null
17
+ ): boolean => {
18
+ if (!nextRecord || typeof nextRecord !== "object") {
19
+ return false;
20
+ }
21
+
22
+ if (!localRecord || typeof localRecord !== "object") {
23
+ return true;
24
+ }
25
+
26
+ if (shouldReplaceWithNextRecord(nextRecord, localRecord)) {
27
+ return true;
28
+ }
29
+
30
+ return (
31
+ typeof localRecord.serverOrigin !== "string" &&
32
+ typeof nextRecord.serverOrigin === "string" &&
33
+ nextRecord.serverOrigin.trim().length > 0
34
+ );
35
+ };
36
+
37
+ export const cacheMergedUserDataThunk = createAsyncThunk<
38
+ void,
39
+ { records: CacheableUserDataRecord[] },
40
+ AppThunkApi
41
+ >("db/cacheMergedUserData", async ({ records }, { extra }) => {
42
+ const clientDb = extra.db;
43
+ if (!clientDb) {
44
+ throw new Error("Client database is not available.");
45
+ }
46
+
47
+ for (const record of records) {
48
+ const dbKey = typeof record?.dbKey === "string" ? record.dbKey.trim() : "";
49
+ if (!dbKey) continue;
50
+
51
+ try {
52
+ const localRecord = await fetchFromClientDb(clientDb, dbKey);
53
+ if (!shouldUpdateLocalUserDataCache(record, localRecord)) {
54
+ continue;
55
+ }
56
+ await clientDb.put(dbKey, record);
57
+ } catch (error) {
58
+ console.warn("[useUserData] Failed to cache merged user data record", {
59
+ dbKey,
60
+ error,
61
+ });
62
+ }
63
+ }
64
+ });