nolo-cli 0.1.41 → 0.1.43

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 (136) hide show
  1. package/README.md +27 -12
  2. package/agent-runtime/agentThread.ts +222 -0
  3. package/agent-runtime/agentThreadAdmission.ts +74 -0
  4. package/agent-runtime/dialogWritePlan.ts +42 -0
  5. package/agent-runtime/hostAdapter.ts +1 -0
  6. package/agent-runtime/index.ts +43 -0
  7. package/agent-runtime/localLoop.ts +31 -2
  8. package/agent-runtime/localToolPolicy.ts +1 -0
  9. package/agent-runtime/localWorkspaceTools.ts +1258 -78
  10. package/agent-runtime/noloWorkspaceTools.ts +20 -1
  11. package/agent-runtime/runtimeToolPolicy.ts +1 -5
  12. package/agent-runtime/runtimeToolSurface.ts +1 -0
  13. package/agentAliases.ts +29 -9
  14. package/agentInternalCommandEntries.ts +11 -3
  15. package/agentMachineCommands.ts +1 -0
  16. package/agentRecordCommands.ts +2 -2
  17. package/agentRecordHelpers.ts +59 -6
  18. package/agentRunCommand.ts +84 -19
  19. package/ai/agent/buildSystemPrompt.ts +14 -0
  20. package/ai/agent/cliExecutor.ts +202 -43
  21. package/ai/agent/createAgentSchema.ts +2 -2
  22. package/ai/agent/liveAudioModel.ts +1 -1
  23. package/ai/agent/streamAgentChatTurn.ts +154 -20
  24. package/ai/agent/streamAgentChatTurnUtils.ts +63 -0
  25. package/ai/ai.locale.ts +70 -0
  26. package/ai/chat/inlineImageUrlsForCustomProvider.ts +7 -2
  27. package/ai/chat/sendOpenAICompletionsRequest.ts +25 -1
  28. package/ai/llm/kimi.ts +0 -1
  29. package/ai/llm/openrouterModels.ts +27 -2
  30. package/ai/memory/policy.ts +49 -0
  31. package/ai/memory/queryShared.ts +10 -4
  32. package/ai/memory/recentRelationshipRecap.ts +4 -0
  33. package/ai/memory/remember.ts +48 -104
  34. package/ai/memory/scope.ts +74 -0
  35. package/ai/memory/types.ts +6 -0
  36. package/ai/memory/understanding.ts +10 -18
  37. package/ai/policy/runtimePolicy.ts +95 -23
  38. package/ai/tools/agent/agentTools.ts +0 -21
  39. package/ai/tools/agent/startAgentDialogTool.ts +2 -1
  40. package/ai/tools/index.ts +2 -20
  41. package/ai/tools/prepareTools.ts +171 -1
  42. package/ai/tools/readXhsProfileTool.ts +80 -69
  43. package/ai/tools/table/createTableTool.ts +11 -0
  44. package/ai/tools/toolPacks.ts +19 -0
  45. package/ai/types.ts +3 -0
  46. package/app/favorite/deletedFavoriteProjection.ts +31 -0
  47. package/app/hooks/deleteDbKey.ts +24 -3
  48. package/app/i18n/translations/interface.locale.ts +48 -16
  49. package/app/types/appSummary.ts +4 -0
  50. package/app/utils/desktopAgentRuntimeTurnClient.ts +2 -0
  51. package/auth/adminPermissions.ts +64 -4
  52. package/auth/routes.ts +5 -0
  53. package/chat/chat.locale.ts +4 -0
  54. package/chat/dialog/actions/handleSendMessageAction.ts +7 -2
  55. package/chat/dialog/dialogAttachmentCleanup.ts +135 -0
  56. package/chat/dialog/dialogSlice.ts +117 -4
  57. package/chat/messages/activityCompletion.ts +97 -0
  58. package/chat/messages/imagePayloadPersistence.ts +22 -0
  59. package/chat/messages/messageContent.ts +20 -16
  60. package/chat/messages/messageSlice.ts +20 -1
  61. package/chat/messages/toolThunks.ts +32 -11
  62. package/chat/messages/types.ts +8 -1
  63. package/chat/messages/web/toolDisplayName.ts +686 -0
  64. package/cli/agentAliases.ts +29 -9
  65. package/cli/agentInternalCommandEntries.ts +11 -3
  66. package/cli/agentMachineCommands.ts +1 -0
  67. package/cli/agentRecordCommands.ts +2 -2
  68. package/cli/agentRecordHelpers.ts +59 -6
  69. package/cli/agentRunCommand.ts +84 -19
  70. package/cli/client/agentRun.ts +65 -253
  71. package/cli/client/localRuntimeAdapter.ts +179 -53
  72. package/cli/commandRegistry.ts +7 -4
  73. package/cli/dialogAttachmentCleanup.ts +151 -0
  74. package/cli/dialogCommands.ts +163 -61
  75. package/cli/dialogInternalCommandEntries.ts +1 -1
  76. package/cli/docCreateCommands.ts +345 -0
  77. package/cli/docInternalCommandEntries.ts +11 -0
  78. package/cli/docPageHelpers.ts +80 -0
  79. package/cli/docSkillHelpers.ts +67 -0
  80. package/cli/docSpaceHelpers.ts +46 -0
  81. package/cli/internalCommandEntries.ts +4 -0
  82. package/cli/machineCommands.ts +18 -34
  83. package/cli/memoryCommands.ts +253 -0
  84. package/cli/memoryInternalCommandEntries.ts +9 -0
  85. package/cli/qoderUsageProbe.ts +252 -0
  86. package/cli/scriptCommandEntries.ts +0 -3
  87. package/cli/secretScan.ts +31 -0
  88. package/cli/tableCommands.ts +248 -13
  89. package/cli/workflowInternalCommandEntries.ts +2 -3
  90. package/client/agentRun.test.ts +68 -84
  91. package/client/agentRun.ts +65 -253
  92. package/client/localRuntimeAdapter.test.ts +505 -74
  93. package/client/localRuntimeAdapter.ts +179 -53
  94. package/commandRegistry.ts +7 -4
  95. package/connector-experimental/capabilities.ts +10 -9
  96. package/create/space/sidebarVisibleTypes.ts +19 -0
  97. package/dialogAttachmentCleanup.ts +151 -0
  98. package/dialogCommands.ts +163 -61
  99. package/dialogInternalCommandEntries.ts +1 -1
  100. package/docCreateCommands.ts +345 -0
  101. package/docInternalCommandEntries.ts +11 -0
  102. package/docPageHelpers.ts +80 -0
  103. package/docSkillHelpers.ts +67 -0
  104. package/docSpaceHelpers.ts +46 -0
  105. package/integrations/google/models.ts +4 -4
  106. package/integrations/openai/generateOpenAIRequestBody.ts +0 -3
  107. package/integrations/xhs-reader/types.ts +36 -3
  108. package/integrations/xhs-reader/url.ts +24 -1
  109. package/internalCommandEntries.ts +4 -0
  110. package/machineCommands.ts +18 -34
  111. package/memoryCommands.ts +253 -0
  112. package/memoryInternalCommandEntries.ts +9 -0
  113. package/package.json +141 -114
  114. package/qoderUsageProbe.ts +252 -0
  115. package/render/contentIcon/types.ts +167 -0
  116. package/render/page/docSlice.ts +30 -3
  117. package/render/page/types.ts +2 -0
  118. package/render/table/createTableAction.ts +4 -0
  119. package/render/table/tableSlice.ts +61 -0
  120. package/render/table/types.ts +10 -0
  121. package/scriptCommandEntries.ts +0 -3
  122. package/secretScan.ts +31 -0
  123. package/server/handlers/agentRun/types.ts +24 -23
  124. package/tableCommands.ts +248 -13
  125. package/workflowInternalCommandEntries.ts +2 -3
  126. package/ai/tools/agent/taskRunTool.ts +0 -112
  127. package/cli/client/taskRunPrompt.ts +0 -27
  128. package/cli/taskRunCommand.ts +0 -265
  129. package/client/taskRunPrompt.ts +0 -27
  130. package/integrations/xhs-reader/analyze/profileAnalyzer.ts +0 -205
  131. package/integrations/xhs-reader/backends/playwrightProfileCollector.ts +0 -624
  132. package/integrations/xhs-reader/bridge/readXhsProfileWithBridge.ts +0 -227
  133. package/integrations/xhs-reader/client/xhsWebApiClient.ts +0 -229
  134. package/integrations/xhs-reader/normalize.ts +0 -390
  135. package/integrations/xhs-reader/orchestrator.ts +0 -178
  136. package/taskRunCommand.ts +0 -265
@@ -43,9 +43,13 @@ import {
43
43
  } from "./hybridRecordStore";
44
44
  import { executeLocalToolWithPolicy } from "./localToolPolicy";
45
45
  import { inferCaptureIntent } from "../../ai/policy/runtimePolicy";
46
+ import { TOOL_PACKS } from "../../ai/tools/toolPacks";
46
47
  import { prepareTools } from "../../ai/tools/prepareTools";
47
- import { taskRunFunctionSchema } from "../../ai/tools/agent/taskRunTool";
48
48
  import {
49
+ LOCAL_CODEX_AGENT_ID,
50
+ LOCAL_CODEX_AGENT_KEY,
51
+ LOCAL_QODER_AGENT_ID,
52
+ LOCAL_QODER_AGENT_KEY,
49
53
  MIMO_MONTH_AGENT_KEY,
50
54
  NOLO_DEFAULT_AGENT_ID,
51
55
  NOLO_DEFAULT_AGENT_KEY,
@@ -62,7 +66,10 @@ import {
62
66
  readXhsProfileFunc,
63
67
  readXhsProfileFunctionSchema,
64
68
  } from "../../ai/tools/readXhsProfileTool";
65
- import { resolveDefaultXhsProfileDir } from "../../integrations/xhs-reader/bridge/readXhsProfileWithBridge";
69
+ import {
70
+ readXPostFunc,
71
+ readXPostFunctionSchema,
72
+ } from "../../ai/tools/readXPostTool";
66
73
 
67
74
  type EnvLike = Record<string, string | undefined>;
68
75
  type FetchInput = Parameters<typeof fetch>[0];
@@ -106,6 +113,7 @@ export type CliLocalRuntimeAdapterDeps = {
106
113
  cwd?: string;
107
114
  output?: { write(chunk: string): unknown };
108
115
  localToolExecutors?: Record<string, (call: any) => Promise<{ content: string; metadata?: Record<string, unknown> }>>;
116
+ readXPost?: typeof readXPostFunc;
109
117
  readXhsProfile?: typeof readXhsProfileFunc;
110
118
  executeCli?: LocalCliExecutor;
111
119
  sleep?: (ms: number) => Promise<void>;
@@ -124,6 +132,55 @@ function resolveLocalUserId(env: EnvLike) {
124
132
  return env.NOLO_LOCAL_USER_ID || env.NOLO_USER_ID || "local";
125
133
  }
126
134
 
135
+ function resolveBuiltinLocalCliAgentConfig(agentRef: string, userId: string): AgentRuntimeAgentConfig | null {
136
+ const normalized = agentRef.trim();
137
+ if (normalized === LOCAL_CODEX_AGENT_KEY || normalized === LOCAL_CODEX_AGENT_ID) {
138
+ return {
139
+ key: LOCAL_CODEX_AGENT_KEY,
140
+ name: "Local Codex",
141
+ prompt: "You are a local Codex CLI coding agent. Use the workspace and dialog evidence available to you, keep changes scoped, run relevant checks, and report worktree, branch, commit or dirty diff, tests, and blockers.",
142
+ apiSource: "cli",
143
+ provider: "cli",
144
+ cliProvider: "codex",
145
+ toolNames: ["readFile", "searchFiles", "execShell"],
146
+ rawRecord: {
147
+ dbKey: LOCAL_CODEX_AGENT_KEY,
148
+ id: LOCAL_CODEX_AGENT_ID,
149
+ userId,
150
+ type: "agent",
151
+ name: "Local Codex",
152
+ apiSource: "cli",
153
+ provider: "cli",
154
+ cliProvider: "codex",
155
+ },
156
+ };
157
+ }
158
+ if (normalized === LOCAL_QODER_AGENT_KEY || normalized === LOCAL_QODER_AGENT_ID) {
159
+ return {
160
+ key: LOCAL_QODER_AGENT_KEY,
161
+ name: "Local Qoder",
162
+ prompt: "You are a local Qoder CLI coding agent. Use the workspace and dialog evidence available to you, keep changes scoped, run relevant checks, and report worktree, branch, commit or dirty diff, tests, and blockers.",
163
+ apiSource: "cli",
164
+ provider: "cli",
165
+ cliProvider: "qoder",
166
+ model: "Qwen3.7-Max",
167
+ toolNames: ["readFile", "searchFiles", "execShell"],
168
+ rawRecord: {
169
+ dbKey: LOCAL_QODER_AGENT_KEY,
170
+ id: LOCAL_QODER_AGENT_ID,
171
+ userId,
172
+ type: "agent",
173
+ name: "Local Qoder",
174
+ apiSource: "cli",
175
+ provider: "cli",
176
+ cliProvider: "qoder",
177
+ model: "Qwen3.7-Max",
178
+ },
179
+ };
180
+ }
181
+ return null;
182
+ }
183
+
127
184
  function parseLocalToolBudgets(env: EnvLike) {
128
185
  const raw = env.NOLO_LOCAL_TOOL_BUDGETS?.trim();
129
186
  if (!raw) return {};
@@ -371,6 +428,25 @@ function shouldExposeLocalPlatformTools(agentKey?: string) {
371
428
  return agentKey !== MIMO_MONTH_AGENT_KEY;
372
429
  }
373
430
 
431
+ function addDefaultLightWebToolsForConfiguredAgents(
432
+ toolNames: string[],
433
+ agentConfig?: AgentRuntimeAgentConfig | null,
434
+ ) {
435
+ const explicitToolNames = Array.isArray((agentConfig as any)?.toolSurface?.explicitToolNames)
436
+ ? (agentConfig as any).toolSurface.explicitToolNames
437
+ : agentConfig?.toolNames;
438
+ if (!Array.isArray(explicitToolNames) || explicitToolNames.length === 0) return toolNames;
439
+ const webCapable = explicitToolNames.some((toolName) =>
440
+ toolName === "fetchWebpage" ||
441
+ toolName === "exa_search" ||
442
+ toolName === "read_x_post" ||
443
+ toolName === "read_xhs_profile" ||
444
+ toolName.startsWith("browser_")
445
+ );
446
+ if (!webCapable) return toolNames;
447
+ return [...new Set([...toolNames, ...TOOL_PACKS.LIGHT_WEB])];
448
+ }
449
+
374
450
  function buildOpenAiTools(args: { agentKey?: string; toolNames?: string[]; env: EnvLike }) {
375
451
  const toolset = buildLocalWorkspaceToolsetForEnv(args);
376
452
  const exposePlatformTools = shouldExposeLocalPlatformTools(args.agentKey);
@@ -378,6 +454,14 @@ function buildOpenAiTools(args: { agentKey?: string; toolNames?: string[]; env:
378
454
  ...buildLocalWorkspaceOpenAiTools({
379
455
  toolNames: toolset.toolNames,
380
456
  exposeShellTools: toolset.exposeShellTools,
457
+ listFilesDescriptionVariant: resolveListFilesDescriptionVariant(args.env),
458
+ listFilesParameterVariant: resolveListFilesParameterVariant(args.env),
459
+ readFileDescriptionVariant: resolveReadFileDescriptionVariant(args.env),
460
+ readFileParameterVariant: resolveReadFileParameterVariant(args.env),
461
+ globFilesDescriptionVariant: resolveGlobFilesDescriptionVariant(args.env),
462
+ globFilesParameterVariant: resolveGlobFilesParameterVariant(args.env),
463
+ searchFilesDescriptionVariant: resolveSearchFilesDescriptionVariant(args.env),
464
+ searchFilesParameterVariant: resolveSearchFilesParameterVariant(args.env),
381
465
  }),
382
466
  ...(exposePlatformTools ? buildServerPlatformOpenAiTools({ toolNames: args.toolNames }) : []),
383
467
  ...(exposePlatformTools ? buildNoloWorkspaceOpenAiTools({ toolNames: args.toolNames }) : []),
@@ -385,44 +469,91 @@ function buildOpenAiTools(args: { agentKey?: string; toolNames?: string[]; env:
385
469
  }
386
470
 
387
471
  function buildLocalWorkspaceToolsetForEnv(args: { toolNames?: string[]; env: EnvLike }) {
388
- void args.env;
389
472
  const toolset = buildLocalWorkspaceToolset({
390
473
  declaredToolNames: args.toolNames,
391
474
  exposeShellTools: true,
475
+ useDeclaredToolNamesOnly: shouldUseDeclaredOnlyLocalWorkspaceTools(args.env),
392
476
  });
393
477
  return toolset;
394
478
  }
395
479
 
396
480
  function buildLocalPolicyToolNames(args: { toolNames?: string[]; env: EnvLike }) {
397
- void args.env;
398
481
  return [
399
482
  ...buildLocalWorkspacePolicyToolNames({
400
483
  declaredToolNames: args.toolNames,
401
484
  exposeShellTools: true,
485
+ useDeclaredToolNamesOnly: shouldUseDeclaredOnlyLocalWorkspaceTools(args.env),
402
486
  }),
403
- ...((args.toolNames ?? []).includes("taskRun") ? ["taskRun"] : []),
487
+ ...((args.toolNames ?? []).includes("read_x_post") ? ["read_x_post"] : []),
404
488
  ...((args.toolNames ?? []).includes("read_xhs_profile") ? ["read_xhs_profile"] : []),
405
489
  ...(args.toolNames ?? []).filter((name) => LOCAL_SERVER_TABLE_TOOL_NAME_SET.has(name)),
406
490
  ...filterNoloWorkspaceToolNames(args.toolNames),
407
491
  ];
408
492
  }
409
493
 
494
+ function shouldUseDeclaredOnlyLocalWorkspaceTools(env: EnvLike) {
495
+ const value = env.NOLO_LOCAL_WORKSPACE_TOOLSET || env.NOLO_LOCAL_TOOLSET_MODE || "";
496
+ return value === "declared-only" || value === "declared";
497
+ }
498
+
499
+ function resolveGlobFilesDescriptionVariant(env: EnvLike) {
500
+ return resolveLocalWorkspaceDescriptionVariant(env.NOLO_GLOBFILES_DESCRIPTION_VARIANT);
501
+ }
502
+
503
+ function resolveListFilesDescriptionVariant(env: EnvLike) {
504
+ return resolveLocalWorkspaceDescriptionVariant(env.NOLO_LISTFILES_DESCRIPTION_VARIANT);
505
+ }
506
+
507
+ function resolveListFilesParameterVariant(env: EnvLike) {
508
+ return resolveLocalWorkspaceParameterVariant(env.NOLO_LISTFILES_PARAMETER_VARIANT);
509
+ }
510
+
511
+ function resolveReadFileDescriptionVariant(env: EnvLike) {
512
+ return resolveLocalWorkspaceDescriptionVariant(env.NOLO_READFILE_DESCRIPTION_VARIANT);
513
+ }
514
+
515
+ function resolveReadFileParameterVariant(env: EnvLike) {
516
+ return resolveLocalWorkspaceParameterVariant(env.NOLO_READFILE_PARAMETER_VARIANT);
517
+ }
518
+
519
+ function resolveGlobFilesParameterVariant(env: EnvLike) {
520
+ return resolveLocalWorkspaceParameterVariant(env.NOLO_GLOBFILES_PARAMETER_VARIANT);
521
+ }
522
+
523
+ function resolveSearchFilesDescriptionVariant(env: EnvLike) {
524
+ return resolveLocalWorkspaceDescriptionVariant(env.NOLO_SEARCHFILES_DESCRIPTION_VARIANT);
525
+ }
526
+
527
+ function resolveSearchFilesParameterVariant(env: EnvLike) {
528
+ return resolveLocalWorkspaceParameterVariant(env.NOLO_SEARCHFILES_PARAMETER_VARIANT);
529
+ }
530
+
531
+ function resolveLocalWorkspaceDescriptionVariant(value: string | undefined) {
532
+ return value === "brief" || value === "strategy" || value === "workflow" || value === "antiShell"
533
+ ? value
534
+ : "strategy";
535
+ }
536
+
537
+ function resolveLocalWorkspaceParameterVariant(value: string | undefined) {
538
+ return value === "minimal" || value === "scoped" || value === "rich" ? value : "rich";
539
+ }
540
+
410
541
  function buildServerPlatformOpenAiTools(args: { toolNames?: string[] }) {
411
542
  const toolNames = new Set(args.toolNames ?? []);
412
543
  const tableTools = prepareTools(
413
544
  (args.toolNames ?? []).filter((name) => LOCAL_SERVER_TABLE_TOOL_NAME_SET.has(name)),
414
545
  );
415
546
  return [
416
- ...(toolNames.has("taskRun")
547
+ ...(toolNames.has("read_xhs_profile")
417
548
  ? [{
418
549
  type: "function",
419
- function: taskRunFunctionSchema,
550
+ function: readXhsProfileFunctionSchema,
420
551
  }]
421
552
  : []),
422
- ...(toolNames.has("read_xhs_profile")
553
+ ...(toolNames.has("read_x_post")
423
554
  ? [{
424
555
  type: "function",
425
- function: readXhsProfileFunctionSchema,
556
+ function: readXPostFunctionSchema,
426
557
  }]
427
558
  : []),
428
559
  ...tableTools,
@@ -555,15 +686,7 @@ function buildServerPlatformToolExecutors(args: {
555
686
  },
556
687
  ]),
557
688
  );
558
- return {
559
- taskRun: async (call: any) => {
560
- return {
561
- content: await postServer("/api/task-run", parseNoloWorkspaceToolArguments(call.arguments)),
562
- metadata: { serverPlatformTool: true },
563
- };
564
- },
565
- ...tableExecutors,
566
- };
689
+ return tableExecutors;
567
690
  }
568
691
 
569
692
  function buildLocalToolExecutors(args: {
@@ -571,34 +694,11 @@ function buildLocalToolExecutors(args: {
571
694
  env: EnvLike;
572
695
  fetchImpl: typeof fetch;
573
696
  localToolExecutors?: CliLocalRuntimeAdapterDeps["localToolExecutors"];
697
+ readXPost?: CliLocalRuntimeAdapterDeps["readXPost"];
574
698
  readXhsProfile?: CliLocalRuntimeAdapterDeps["readXhsProfile"];
575
699
  commandTimeoutMs?: number;
576
700
  commandOutputLimit?: number;
577
701
  }) {
578
- const resolveCliDefaultXhsArgs = (parsedArgs: any) => {
579
- if (
580
- parsedArgs?.profileDir ||
581
- parsedArgs?.profile_dir ||
582
- args.env.NOLO_XHS_READER_PROFILE_DIR
583
- ) {
584
- return parsedArgs;
585
- }
586
- return {
587
- ...parsedArgs,
588
- profileDir: resolveDefaultXhsProfileDir({
589
- env: {
590
- ...process.env,
591
- ...args.env,
592
- NOLO_DESKTOP: "1",
593
- NOLO_DESKTOP_CHANNEL:
594
- args.env.NOLO_DESKTOP_CHANNEL?.trim() ||
595
- args.env.NOLO_XHS_READER_DESKTOP_CHANNEL?.trim() ||
596
- "stable",
597
- },
598
- }),
599
- };
600
- };
601
-
602
702
  return {
603
703
  ...createLocalWorkspaceToolExecutors({
604
704
  workspaceRoot: args.workspaceRoot,
@@ -612,12 +712,28 @@ function buildLocalToolExecutors(args: {
612
712
  ...buildCliWorkspaceToolExecutors({
613
713
  env: args.env,
614
714
  }),
715
+ read_x_post: async (call: any) => {
716
+ const parsedArgs = (() => {
717
+ try { return JSON.parse(call.arguments || "{}"); } catch { return {}; }
718
+ })();
719
+ const result = await (args.readXPost ?? readXPostFunc)(
720
+ parsedArgs,
721
+ undefined,
722
+ );
723
+ return {
724
+ content: JSON.stringify(result.rawData),
725
+ metadata: {
726
+ xPostLocalBridge: true,
727
+ displayData: result.displayData,
728
+ },
729
+ };
730
+ },
615
731
  read_xhs_profile: async (call: any) => {
616
732
  const parsedArgs = (() => {
617
733
  try { return JSON.parse(call.arguments || "{}"); } catch { return {}; }
618
734
  })();
619
735
  const result = await (args.readXhsProfile ?? readXhsProfileFunc)(
620
- resolveCliDefaultXhsArgs(parsedArgs),
736
+ parsedArgs,
621
737
  undefined,
622
738
  );
623
739
  return {
@@ -713,6 +829,7 @@ export function createCliLocalRuntimeAdapter(
713
829
  env: deps.env,
714
830
  fetchImpl,
715
831
  localToolExecutors: deps.localToolExecutors,
832
+ readXPost: deps.readXPost,
716
833
  readXhsProfile: deps.readXhsProfile,
717
834
  ...runtimeToolExecutionLimits,
718
835
  });
@@ -721,16 +838,22 @@ export function createCliLocalRuntimeAdapter(
721
838
  host: "cli",
722
839
  capabilities: ["leveldb-agent-config", "local-provider", "leveldb-persistence", "local-tools"],
723
840
  loadAgentConfig: async (agentRef) => {
841
+ const storedAgentConfig = await readAgentFromStore({
842
+ agentRef,
843
+ store: await resolveStore(deps),
844
+ userId,
845
+ });
846
+ const fallbackLocalCliAgentConfig =
847
+ storedAgentConfig ? null : resolveBuiltinLocalCliAgentConfig(agentRef, userId);
724
848
  const agentConfig = withResolvedRuntimeToolSurface(
725
- await readAgentFromStore({
726
- agentRef,
727
- store: await resolveStore(deps),
728
- userId,
729
- }),
849
+ storedAgentConfig ?? fallbackLocalCliAgentConfig,
730
850
  deps.env
731
851
  );
732
852
  const requestedToolNames = agentConfig
733
- ? resolveRequestedRuntimeToolNames({ agentConfig })
853
+ ? addDefaultLightWebToolsForConfiguredAgents(
854
+ resolveRequestedRuntimeToolNames({ agentConfig }),
855
+ agentConfig,
856
+ )
734
857
  : [];
735
858
  activeAgentToolNames = buildLocalPolicyToolNames({
736
859
  toolNames: requestedToolNames,
@@ -744,6 +867,7 @@ export function createCliLocalRuntimeAdapter(
744
867
  env: deps.env,
745
868
  fetchImpl,
746
869
  localToolExecutors: deps.localToolExecutors,
870
+ readXPost: deps.readXPost,
747
871
  readXhsProfile: deps.readXhsProfile,
748
872
  ...runtimeToolExecutionLimits,
749
873
  });
@@ -828,9 +952,10 @@ export function createCliLocalRuntimeAdapter(
828
952
  return {
829
953
  model: providerConfig.model,
830
954
  complete: async (messages, options) => {
831
- const requestedToolNames = resolveRequestedRuntimeToolNames({
955
+ const requestedToolNames = addDefaultLightWebToolsForConfiguredAgents(
956
+ resolveRequestedRuntimeToolNames({ agentConfig }),
832
957
  agentConfig,
833
- });
958
+ );
834
959
  const tools = buildOpenAiTools({
835
960
  agentKey: agentConfig.key,
836
961
  toolNames: requestedToolNames,
@@ -901,9 +1026,10 @@ export function createCliLocalRuntimeAdapter(
901
1026
  return {
902
1027
  model: providerConfig.model,
903
1028
  complete: async (messages, options) => {
904
- const requestedToolNames = resolveRequestedRuntimeToolNames({
1029
+ const requestedToolNames = addDefaultLightWebToolsForConfiguredAgents(
1030
+ resolveRequestedRuntimeToolNames({ agentConfig }),
905
1031
  agentConfig,
906
- });
1032
+ );
907
1033
  const tools = buildOpenAiTools({
908
1034
  agentKey: agentConfig.key,
909
1035
  toolNames: requestedToolNames,
@@ -14,6 +14,7 @@ export const COMMANDS: CommandEntry[] = [
14
14
  ];
15
15
 
16
16
  export const GROUP_ORDER = [
17
+ "run",
17
18
  "chat",
18
19
  "doc",
19
20
  "skill-doc",
@@ -21,9 +22,9 @@ export const GROUP_ORDER = [
21
22
  "agent",
22
23
  "doctor",
23
24
  "dialog",
25
+ "memory",
24
26
  "space",
25
27
  "table",
26
- "task-run",
27
28
  "llama",
28
29
  "model-runtime",
29
30
  "dev",
@@ -55,23 +56,25 @@ export function renderHelpText() {
55
56
  " nolo doctor",
56
57
  " nolo doctor runtime",
57
58
  " nolo update",
59
+ ' nolo run "review this repository" # no Nolo login required; uses local Codex CLI',
58
60
  ' nolo doc create --title "Trip Notes" --body "hello" --sync local,us --dry-run',
59
61
  ' nolo skill-doc create --title "Agent Query Skill" --description "Inspect recent agent dialogs" --sync local,main,us',
60
62
  " nolo agent list --json",
61
63
  " nolo space list --json",
62
64
  " nolo agent pull agent-pub-01APPBUILDER00000001YAII3I",
63
- " nolo agent read agent-pub-01APPBUILDER00000001YAII3I",
64
- ' nolo agent run frontend-implementer --msg "polish notifications"',
65
+ " nolo agent read agent-pub-01APPBUILDER00000001YAII3I",
66
+ " nolo agent usage qoder",
67
+ ' nolo agent run frontend-implementer --msg "polish notifications"',
65
68
  " nolo agent bind-current agent-user-1-agent-1",
66
69
  " nolo agent runtime-doctor agent-user-1-agent-1",
67
70
  ' nolo agent smoke-current agent-user-1-agent-1 --msg "ping"',
68
71
  ' nolo chat --agent agent-pub-01APPBUILDER00000001YAII3I --msg "你好"',
72
+ " nolo memory delete --source-dialog 01ABC... --yes --json",
69
73
  " nolo space read 01KKY77TT0DA9NY7TNW3R7255N --content-key page-user-id --brief",
70
74
  " nolo space delete --name-prefix rn_owner_verify_0504 --yes",
71
75
  " nolo table query --table 01ABCXYZ",
72
76
  ' nolo table query --table meta-0e95801d90-NOLOTASKBOARD --columns \'["title","status","owner","priority","codeStatus"]\' --no-base-fields --output items',
73
77
  ' nolo table update-row --table meta-0e95801d90-NOLOTASKBOARD --row 01ROWID --changes \'{"status":"已完成"}\'',
74
- " nolo task-run read-context --row-dbkey row-user-table-row",
75
78
  " nolo llama status",
76
79
  ];
77
80
 
@@ -0,0 +1,151 @@
1
+ import { deleteDbRecordOnServers, readDbRecordFromServers, type GlobalDeleteResult } from "./globalRecordOperations";
2
+ import {
3
+ buildDialogAttachmentPlan,
4
+ extractFileContentIds,
5
+ type DialogAttachmentCandidate,
6
+ type DialogAttachmentPlan,
7
+ } from "../chat/dialog/dialogAttachmentCleanup";
8
+
9
+ export { buildDialogAttachmentPlan, extractFileContentIds };
10
+
11
+ export type DialogAttachmentDeleteResult = {
12
+ fileId: string;
13
+ fileDbKey: string;
14
+ deleteResults: GlobalDeleteResult[];
15
+ };
16
+
17
+ function asText(value: unknown) {
18
+ return typeof value === "string" && value.trim() ? value.trim() : null;
19
+ }
20
+
21
+ async function readDialogMessages(args: {
22
+ authToken: string;
23
+ dialogId: string;
24
+ fetchImpl: typeof fetch;
25
+ serverUrl: string;
26
+ }) {
27
+ const response = await args.fetchImpl(`${args.serverUrl}/rpc/getConvMsgs`, {
28
+ method: "POST",
29
+ headers: {
30
+ Authorization: `Bearer ${args.authToken}`,
31
+ "Content-Type": "application/json",
32
+ },
33
+ body: JSON.stringify({ dialogId: args.dialogId, limit: 10000 }),
34
+ });
35
+ if (!response.ok) {
36
+ throw new Error(`read dialog messages failed on ${args.serverUrl}: HTTP ${response.status}`);
37
+ }
38
+ const payload = await response.json();
39
+ return Array.isArray(payload) ? payload : [];
40
+ }
41
+
42
+ export async function planDialogAttachmentCleanup(args: {
43
+ authToken: string;
44
+ dialogId: string;
45
+ ownerId?: string | null;
46
+ includeUserOwnedReferenced?: boolean;
47
+ fallbackFetchImpl?: typeof fetch;
48
+ fetchImpl: typeof fetch;
49
+ serverUrl: string;
50
+ serverUrls: string[];
51
+ }): Promise<DialogAttachmentPlan> {
52
+ const messages = await readDialogMessages({
53
+ authToken: args.authToken,
54
+ dialogId: args.dialogId,
55
+ fetchImpl: args.fetchImpl,
56
+ serverUrl: args.serverUrl,
57
+ });
58
+ const fileIds = [...new Set(messages.flatMap((message) => extractFileContentIds(message)))];
59
+ const metadataByFileId: Record<string, any> = {};
60
+ const metadataReadFailures: Array<{ fileId: string; error: string }> = [];
61
+
62
+ for (const fileId of fileIds) {
63
+ try {
64
+ const result = await readDbRecordFromServers({
65
+ authToken: args.authToken,
66
+ dbKey: `file-id-${fileId}`,
67
+ fallbackFetchImpl: args.fallbackFetchImpl,
68
+ fetchImpl: args.fetchImpl,
69
+ serverUrls: args.serverUrls,
70
+ label: `file metadata index ${fileId}`,
71
+ });
72
+ const mainKey = asText(result.record?.mainKey);
73
+ if (!mainKey) throw new Error(`file-id index missing mainKey`);
74
+ const metadata = await readDbRecordFromServers({
75
+ authToken: args.authToken,
76
+ dbKey: mainKey,
77
+ fallbackFetchImpl: args.fallbackFetchImpl,
78
+ fetchImpl: args.fetchImpl,
79
+ serverUrls: args.serverUrls,
80
+ label: `file metadata ${fileId}`,
81
+ });
82
+ metadataByFileId[fileId] = {
83
+ ...metadata.record,
84
+ dbKey: asText(metadata.record?.dbKey) ?? mainKey,
85
+ };
86
+ } catch (error) {
87
+ const fallbackKey =
88
+ args.ownerId && !fileId.startsWith("file-")
89
+ ? `file-${args.ownerId}-${fileId}`
90
+ : null;
91
+ if (fallbackKey) {
92
+ try {
93
+ const metadata = await readDbRecordFromServers({
94
+ authToken: args.authToken,
95
+ dbKey: fallbackKey,
96
+ fallbackFetchImpl: args.fallbackFetchImpl,
97
+ fetchImpl: args.fetchImpl,
98
+ serverUrls: args.serverUrls,
99
+ label: `file metadata ${fileId}`,
100
+ });
101
+ metadataByFileId[fileId] = {
102
+ ...metadata.record,
103
+ dbKey: asText(metadata.record?.dbKey) ?? fallbackKey,
104
+ };
105
+ continue;
106
+ } catch {
107
+ // Keep the original index error; it explains why the preferred path failed.
108
+ }
109
+ }
110
+ metadataReadFailures.push({
111
+ fileId,
112
+ error: error instanceof Error ? error.message : String(error),
113
+ });
114
+ }
115
+ }
116
+
117
+ return buildDialogAttachmentPlan({
118
+ dialogId: args.dialogId,
119
+ ownerId: args.ownerId,
120
+ includeUserOwnedReferenced: args.includeUserOwnedReferenced,
121
+ messages,
122
+ metadataByFileId,
123
+ metadataReadFailures,
124
+ });
125
+ }
126
+
127
+ export async function deleteDialogAttachmentCandidates(args: {
128
+ authToken: string;
129
+ candidates: DialogAttachmentCandidate[];
130
+ fallbackFetchImpl?: typeof fetch;
131
+ fetchImpl: typeof fetch;
132
+ serverUrls: string[];
133
+ }): Promise<DialogAttachmentDeleteResult[]> {
134
+ const results: DialogAttachmentDeleteResult[] = [];
135
+ for (const candidate of args.candidates) {
136
+ if (candidate.status !== "delete" || !candidate.fileDbKey) continue;
137
+ const deleteResults = await deleteDbRecordOnServers({
138
+ authToken: args.authToken,
139
+ dbKey: candidate.fileDbKey,
140
+ fallbackFetchImpl: args.fallbackFetchImpl,
141
+ fetchImpl: args.fetchImpl,
142
+ serverUrls: args.serverUrls,
143
+ });
144
+ results.push({
145
+ fileId: candidate.fileId,
146
+ fileDbKey: candidate.fileDbKey,
147
+ deleteResults,
148
+ });
149
+ }
150
+ return results;
151
+ }