@nocobase/app-plugin-agents 1.0.0-beta.20 → 1.0.0-beta.21

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 (186) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/client/chat/agent-mode.d.ts +47 -0
  3. package/dist/client/chat/agent-mode.d.ts.map +1 -0
  4. package/dist/client/chat/agent-mode.js +78 -0
  5. package/dist/client/chat/agent-mode.js.map +1 -0
  6. package/dist/client/chat/api.d.ts +2 -1
  7. package/dist/client/chat/api.d.ts.map +1 -1
  8. package/dist/client/chat/api.js +21 -5
  9. package/dist/client/chat/api.js.map +1 -1
  10. package/dist/client/chat/controller.d.ts +26 -0
  11. package/dist/client/chat/controller.d.ts.map +1 -1
  12. package/dist/client/chat/controller.js +328 -7
  13. package/dist/client/chat/controller.js.map +1 -1
  14. package/dist/client/chat/message-model.d.ts +10 -2
  15. package/dist/client/chat/message-model.d.ts.map +1 -1
  16. package/dist/client/chat/message-model.js +19 -2
  17. package/dist/client/chat/message-model.js.map +1 -1
  18. package/dist/client/chat/panel-state.d.ts +3 -0
  19. package/dist/client/chat/panel-state.d.ts.map +1 -1
  20. package/dist/client/chat/panel-state.js +4 -6
  21. package/dist/client/chat/panel-state.js.map +1 -1
  22. package/dist/client/chat/provider.d.ts +13 -2
  23. package/dist/client/chat/provider.d.ts.map +1 -1
  24. package/dist/client/chat/provider.js +25 -2
  25. package/dist/client/chat/provider.js.map +1 -1
  26. package/dist/client/chat/use-chat.d.ts +8 -1
  27. package/dist/client/chat/use-chat.d.ts.map +1 -1
  28. package/dist/client/chat/use-chat.js +28 -4
  29. package/dist/client/chat/use-chat.js.map +1 -1
  30. package/dist/client/chat/use-conversation-model.d.ts +4 -1
  31. package/dist/client/chat/use-conversation-model.d.ts.map +1 -1
  32. package/dist/client/chat/use-conversation-model.js +4 -3
  33. package/dist/client/chat/use-conversation-model.js.map +1 -1
  34. package/dist/client/chat.d.ts +6 -0
  35. package/dist/client/chat.d.ts.map +1 -1
  36. package/dist/client/chat.js +2 -0
  37. package/dist/client/chat.js.map +1 -1
  38. package/dist/client/online/service-editor.js +6 -1
  39. package/dist/client/online/service-editor.js.map +1 -1
  40. package/dist/package.json +17 -1
  41. package/dist/server/composition.d.ts.map +1 -1
  42. package/dist/server/composition.js +4 -0
  43. package/dist/server/composition.js.map +1 -1
  44. package/dist/server/core/conversations/attachments.d.ts +1 -1
  45. package/dist/server/core/conversations/attachments.d.ts.map +1 -1
  46. package/dist/server/core/conversations/attachments.js +2 -2
  47. package/dist/server/core/conversations/attachments.js.map +1 -1
  48. package/dist/server/core/conversations/conversation.service.d.ts.map +1 -1
  49. package/dist/server/core/conversations/conversation.service.js +7 -0
  50. package/dist/server/core/conversations/conversation.service.js.map +1 -1
  51. package/dist/server/index.d.ts +1 -0
  52. package/dist/server/index.d.ts.map +1 -1
  53. package/dist/server/index.js.map +1 -1
  54. package/dist/server/kernel/events.d.ts +9 -0
  55. package/dist/server/kernel/events.d.ts.map +1 -1
  56. package/dist/server/kernel/events.js.map +1 -1
  57. package/dist/server/online/agent/configuration.d.ts +2 -0
  58. package/dist/server/online/agent/configuration.d.ts.map +1 -1
  59. package/dist/server/online/agent/configuration.js +21 -2
  60. package/dist/server/online/agent/configuration.js.map +1 -1
  61. package/dist/server/online/agent/contracts.d.ts +5 -0
  62. package/dist/server/online/agent/contracts.d.ts.map +1 -1
  63. package/dist/server/online/agent/index.d.ts +1 -0
  64. package/dist/server/online/agent/index.d.ts.map +1 -1
  65. package/dist/server/online/agent/index.js.map +1 -1
  66. package/dist/server/online/agent/messages/resend.d.ts +7 -0
  67. package/dist/server/online/agent/messages/resend.d.ts.map +1 -0
  68. package/dist/server/online/agent/messages/resend.js +58 -0
  69. package/dist/server/online/agent/messages/resend.js.map +1 -0
  70. package/dist/server/online/agent/native/backend.d.ts +4 -0
  71. package/dist/server/online/agent/native/backend.d.ts.map +1 -1
  72. package/dist/server/online/agent/native/backend.js +17 -24
  73. package/dist/server/online/agent/native/backend.js.map +1 -1
  74. package/dist/server/online/agent/native/execution.d.ts +6 -12
  75. package/dist/server/online/agent/native/execution.d.ts.map +1 -1
  76. package/dist/server/online/agent/native/execution.js +38 -11
  77. package/dist/server/online/agent/native/execution.js.map +1 -1
  78. package/dist/server/online/agent/native/session.d.ts +2 -0
  79. package/dist/server/online/agent/native/session.d.ts.map +1 -1
  80. package/dist/server/online/agent/native/session.js +11 -0
  81. package/dist/server/online/agent/native/session.js.map +1 -1
  82. package/dist/server/online/agent/native/types.d.ts +20 -0
  83. package/dist/server/online/agent/native/types.d.ts.map +1 -1
  84. package/dist/server/online/agent/persistence/store.d.ts +17 -0
  85. package/dist/server/online/agent/persistence/store.d.ts.map +1 -1
  86. package/dist/server/online/agent/persistence/store.js +118 -17
  87. package/dist/server/online/agent/persistence/store.js.map +1 -1
  88. package/dist/server/online/agent/runner/backend.d.ts +1 -0
  89. package/dist/server/online/agent/runner/backend.d.ts.map +1 -1
  90. package/dist/server/online/agent/runner/backend.js +3 -0
  91. package/dist/server/online/agent/runner/backend.js.map +1 -1
  92. package/dist/server/online/agent/service.d.ts +2 -0
  93. package/dist/server/online/agent/service.d.ts.map +1 -1
  94. package/dist/server/online/agent/service.js +2 -0
  95. package/dist/server/online/agent/service.js.map +1 -1
  96. package/dist/server/online/agent/types.d.ts +5 -0
  97. package/dist/server/online/agent/types.d.ts.map +1 -1
  98. package/dist/server/online/agent/web-search.d.ts +13 -0
  99. package/dist/server/online/agent/web-search.d.ts.map +1 -0
  100. package/dist/server/online/agent/web-search.js +65 -0
  101. package/dist/server/online/agent/web-search.js.map +1 -0
  102. package/dist/server/online/gateway.d.ts +13 -0
  103. package/dist/server/online/gateway.d.ts.map +1 -1
  104. package/dist/server/online/gateway.js +116 -2
  105. package/dist/server/online/gateway.js.map +1 -1
  106. package/dist/server/online/index.d.ts +1 -1
  107. package/dist/server/online/index.d.ts.map +1 -1
  108. package/dist/server/online/index.js.map +1 -1
  109. package/dist/server/online/providers.d.ts +6 -0
  110. package/dist/server/online/providers.d.ts.map +1 -1
  111. package/dist/server/online/providers.js +29 -1
  112. package/dist/server/online/providers.js.map +1 -1
  113. package/dist/server/online/search/alibaba.d.ts +10 -0
  114. package/dist/server/online/search/alibaba.d.ts.map +1 -0
  115. package/dist/server/online/search/alibaba.js +34 -0
  116. package/dist/server/online/search/alibaba.js.map +1 -0
  117. package/dist/server/online/search/anthropic.d.ts +12 -0
  118. package/dist/server/online/search/anthropic.d.ts.map +1 -0
  119. package/dist/server/online/search/anthropic.js +62 -0
  120. package/dist/server/online/search/anthropic.js.map +1 -0
  121. package/dist/server/online/search/budget.d.ts +13 -0
  122. package/dist/server/online/search/budget.d.ts.map +1 -0
  123. package/dist/server/online/search/budget.js +63 -0
  124. package/dist/server/online/search/budget.js.map +1 -0
  125. package/dist/server/online/search/capabilities.d.ts +7 -0
  126. package/dist/server/online/search/capabilities.d.ts.map +1 -0
  127. package/dist/server/online/search/capabilities.js +75 -0
  128. package/dist/server/online/search/capabilities.js.map +1 -0
  129. package/dist/server/online/search/contracts.d.ts +20 -0
  130. package/dist/server/online/search/contracts.d.ts.map +1 -0
  131. package/dist/server/online/search/contracts.js +2 -0
  132. package/dist/server/online/search/contracts.js.map +1 -0
  133. package/dist/server/online/search/google.d.ts +12 -0
  134. package/dist/server/online/search/google.d.ts.map +1 -0
  135. package/dist/server/online/search/google.js +51 -0
  136. package/dist/server/online/search/google.js.map +1 -0
  137. package/dist/server/online/search/openai.d.ts +18 -0
  138. package/dist/server/online/search/openai.d.ts.map +1 -0
  139. package/dist/server/online/search/openai.js +51 -0
  140. package/dist/server/online/search/openai.js.map +1 -0
  141. package/dist/server/online/search/prompts.d.ts +5 -0
  142. package/dist/server/online/search/prompts.d.ts.map +1 -0
  143. package/dist/server/online/search/prompts.js +28 -0
  144. package/dist/server/online/search/prompts.js.map +1 -0
  145. package/dist/server/online/search/results.d.ts +2 -0
  146. package/dist/server/online/search/results.d.ts.map +1 -0
  147. package/dist/server/online/search/results.js +2 -0
  148. package/dist/server/online/search/results.js.map +1 -0
  149. package/dist/server/online/services.d.ts +2 -0
  150. package/dist/server/online/services.d.ts.map +1 -1
  151. package/dist/server/online/services.js +17 -2
  152. package/dist/server/online/services.js.map +1 -1
  153. package/dist/server/providers/agents.d.ts.map +1 -1
  154. package/dist/server/providers/agents.js +11 -0
  155. package/dist/server/providers/agents.js.map +1 -1
  156. package/dist/server/routes/chat.d.ts.map +1 -1
  157. package/dist/server/routes/chat.js +34 -1
  158. package/dist/server/routes/chat.js.map +1 -1
  159. package/dist/server/routes/schemas.d.ts +3 -0
  160. package/dist/server/routes/schemas.d.ts.map +1 -1
  161. package/dist/server/routes/schemas.js +9 -0
  162. package/dist/server/routes/schemas.js.map +1 -1
  163. package/dist/shared/conversations.d.ts +12 -0
  164. package/dist/shared/conversations.d.ts.map +1 -1
  165. package/dist/shared/conversations.js.map +1 -1
  166. package/dist/shared/models.d.ts +9 -0
  167. package/dist/shared/models.d.ts.map +1 -1
  168. package/dist/shared/models.js.map +1 -1
  169. package/dist/shared/resend.d.ts +10 -0
  170. package/dist/shared/resend.d.ts.map +1 -0
  171. package/dist/shared/resend.js +2 -0
  172. package/dist/shared/resend.js.map +1 -0
  173. package/dist/shared/web-search-results.d.ts +16 -0
  174. package/dist/shared/web-search-results.d.ts.map +1 -0
  175. package/dist/shared/web-search-results.js +103 -0
  176. package/dist/shared/web-search-results.js.map +1 -0
  177. package/dist/shared/web-search.d.ts +40 -0
  178. package/dist/shared/web-search.d.ts.map +1 -0
  179. package/dist/shared/web-search.js +15 -0
  180. package/dist/shared/web-search.js.map +1 -0
  181. package/docs/message-resend.md +39 -0
  182. package/docs/native-web-search.md +76 -0
  183. package/docs/ui-message-clients.md +7 -1
  184. package/package.json +9 -1
  185. package/skills/nocobase-app-plugin-agents/SKILL.md +2 -1
  186. package/skills/nocobase-app-plugin-agents/references/online-agent.md +9 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resend.js","sourceRoot":"","sources":["../../shared/resend.ts"],"names":[],"mappings":""}
@@ -0,0 +1,16 @@
1
+ import { z } from 'zod';
2
+ import type { WebSearchInput, WebSearchItem, WebSearchResult, WebSearchSource } from './web-search.js';
3
+ export declare const WebSearchInputSchema: z.ZodType<WebSearchInput>;
4
+ export declare function safeSearchSource(value: {
5
+ title?: string;
6
+ url: string;
7
+ }): WebSearchSource | null;
8
+ export declare function searchItem(query: string, text: string, sources: readonly {
9
+ title?: string;
10
+ url: string;
11
+ }[], executed: boolean): WebSearchItem;
12
+ /** Alibaba may return useful text without proving that its search switch was acted on. */
13
+ export declare function unverifiedSearchItem(query: string, text: string): WebSearchItem;
14
+ /** Public/history/model projections reconstruct only known safe evidence, never the raw provider body. */
15
+ export declare function publicSearchResult(value: unknown): WebSearchResult;
16
+ //# sourceMappingURL=web-search-results.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-search-results.d.ts","sourceRoot":"","sources":["../../shared/web-search-results.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,eAAe,EACf,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAOzB,eAAO,MAAM,oBAAoB,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAezD,CAAC;AAEH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb,GAAG,eAAe,GAAG,IAAI,CAqBzB;AAED,wBAAgB,UAAU,CACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,SAAS;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,EACnD,QAAQ,EAAE,OAAO,GAChB,aAAa,CAoBf;AAED,0FAA0F;AAC1F,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,aAAa,CAKf;AA2BD,0GAA0G;AAC1G,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAuBlE"}
@@ -0,0 +1,103 @@
1
+ import { z } from 'zod';
2
+ import { WEB_SEARCH_LIMITS } from './web-search.js';
3
+ const characters = (value) => [...value].length;
4
+ const limited = (value, max) => [...value].slice(0, max).join('');
5
+ export const WebSearchInputSchema = z.strictObject({
6
+ queries: z
7
+ .array(z
8
+ .string()
9
+ .trim()
10
+ .refine((value) => characters(value) >= 1 &&
11
+ characters(value) <= WEB_SEARCH_LIMITS.queryCharacters))
12
+ .min(1)
13
+ .max(WEB_SEARCH_LIMITS.queriesPerCall)
14
+ .transform((queries) => [...new Set(queries)]),
15
+ });
16
+ export function safeSearchSource(value) {
17
+ if (characters(value.url) > WEB_SEARCH_LIMITS.urlCharacters)
18
+ return null;
19
+ try {
20
+ const url = new URL(value.url);
21
+ if (characters(url.href) > WEB_SEARCH_LIMITS.urlCharacters)
22
+ return null;
23
+ if (!['https:', 'http:'].includes(url.protocol) ||
24
+ url.username ||
25
+ url.password)
26
+ return null;
27
+ return {
28
+ title: limited(value.title?.trim() || url.hostname, WEB_SEARCH_LIMITS.titleCharacters),
29
+ url: url.href,
30
+ };
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ export function searchItem(query, text, sources, executed) {
37
+ const safe = [];
38
+ const seen = new Set();
39
+ for (const value of sources) {
40
+ const source = safeSearchSource(value);
41
+ if (!source || seen.has(source.url))
42
+ continue;
43
+ safe.push(source);
44
+ seen.add(source.url);
45
+ if (safe.length === WEB_SEARCH_LIMITS.sources)
46
+ break;
47
+ }
48
+ if (!executed || !safe.length)
49
+ return { query, status: 'failed', reason: 'noEvidence' };
50
+ return {
51
+ query,
52
+ status: 'success',
53
+ findings: limited(text.trim(), WEB_SEARCH_LIMITS.findingsCharacters) ||
54
+ 'Sources were retrieved; no summary was returned.',
55
+ sources: safe,
56
+ };
57
+ }
58
+ /** Alibaba may return useful text without proving that its search switch was acted on. */
59
+ export function unverifiedSearchItem(query, text) {
60
+ const findings = limited(text.trim(), WEB_SEARCH_LIMITS.findingsCharacters);
61
+ return findings
62
+ ? { query, status: 'unverified', findings, sources: [] }
63
+ : { query, status: 'failed', reason: 'noEvidence' };
64
+ }
65
+ const ResultSchema = z.object({
66
+ results: z
67
+ .array(z.discriminatedUnion('status', [
68
+ z.object({
69
+ query: z.string(),
70
+ status: z.literal('success'),
71
+ findings: z.string(),
72
+ sources: z.array(z.object({ title: z.string(), url: z.string() })),
73
+ }),
74
+ z.object({
75
+ query: z.string(),
76
+ status: z.literal('unverified'),
77
+ findings: z.string(),
78
+ sources: z.tuple([]),
79
+ }),
80
+ z.object({
81
+ query: z.string(),
82
+ status: z.literal('failed'),
83
+ reason: z.enum(['timeout', 'unavailable', 'noEvidence']),
84
+ }),
85
+ ]))
86
+ .max(WEB_SEARCH_LIMITS.queriesPerCall),
87
+ });
88
+ /** Public/history/model projections reconstruct only known safe evidence, never the raw provider body. */
89
+ export function publicSearchResult(value) {
90
+ const parsed = ResultSchema.parse(value);
91
+ return {
92
+ results: parsed.results.map((item) => item.status === 'success'
93
+ ? searchItem(limited(item.query, WEB_SEARCH_LIMITS.queryCharacters), item.findings, item.sources, true)
94
+ : item.status === 'unverified'
95
+ ? unverifiedSearchItem(limited(item.query, WEB_SEARCH_LIMITS.queryCharacters), item.findings)
96
+ : {
97
+ query: limited(item.query, WEB_SEARCH_LIMITS.queryCharacters),
98
+ status: item.status,
99
+ reason: item.reason,
100
+ }),
101
+ };
102
+ }
103
+ //# sourceMappingURL=web-search-results.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-search-results.js","sourceRoot":"","sources":["../../shared/web-search-results.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /** The ordinary Native OnlineAgent tool; provider-hosted tools stay internal. */
2
+ export declare const WEB_SEARCH_TOOL = "webSearch";
3
+ export declare const WEB_SEARCH_LIMITS: Readonly<{
4
+ queriesPerCall: number;
5
+ queriesPerExecution: number;
6
+ concurrency: number;
7
+ totalTimeoutMs: number;
8
+ queryTimeoutMs: number;
9
+ queryCharacters: number;
10
+ findingsCharacters: number;
11
+ sources: number;
12
+ titleCharacters: number;
13
+ urlCharacters: number;
14
+ }>;
15
+ export type WebSearchInput = {
16
+ queries: string[];
17
+ };
18
+ export type WebSearchSource = {
19
+ title: string;
20
+ url: string;
21
+ };
22
+ export type WebSearchItem = {
23
+ query: string;
24
+ status: 'success';
25
+ findings: string;
26
+ sources: WebSearchSource[];
27
+ } | {
28
+ query: string;
29
+ status: 'unverified';
30
+ findings: string;
31
+ sources: [];
32
+ } | {
33
+ query: string;
34
+ status: 'failed';
35
+ reason: 'timeout' | 'unavailable' | 'noEvidence';
36
+ };
37
+ export type WebSearchResult = {
38
+ results: WebSearchItem[];
39
+ };
40
+ //# sourceMappingURL=web-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-search.d.ts","sourceRoot":"","sources":["../../shared/web-search.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC;IACvC,cAAc,EAAE,MAAM,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,CAAC;CACvB,CAWA,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AACnD,MAAM,MAAM,eAAe,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAC7D,MAAM,MAAM,aAAa,GACrB;IACE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B,GACD;IACE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,EAAE,CAAC;CACb,GACD;IACE,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,QAAQ,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,YAAY,CAAC;CAClD,CAAC;AACN,MAAM,MAAM,eAAe,GAAG;IAAE,OAAO,EAAE,aAAa,EAAE,CAAA;CAAE,CAAC"}
@@ -0,0 +1,15 @@
1
+ /** The ordinary Native OnlineAgent tool; provider-hosted tools stay internal. */
2
+ export const WEB_SEARCH_TOOL = 'webSearch';
3
+ export const WEB_SEARCH_LIMITS = {
4
+ queriesPerCall: 4,
5
+ queriesPerExecution: 8,
6
+ concurrency: 2,
7
+ totalTimeoutMs: 60_000,
8
+ queryTimeoutMs: 30_000,
9
+ queryCharacters: 1000,
10
+ findingsCharacters: 6000,
11
+ sources: 10,
12
+ titleCharacters: 300,
13
+ urlCharacters: 4096,
14
+ };
15
+ //# sourceMappingURL=web-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web-search.js","sourceRoot":"","sources":["../../shared/web-search.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,CAAC;AAE3C,MAAM,CAAC,MAAM,iBAAiB,GAWzB;IACH,cAAc,EAAE,CAAC;IACjB,mBAAmB,EAAE,CAAC;IACtB,WAAW,EAAE,CAAC;IACd,cAAc,EAAE,MAAM;IACtB,cAAc,EAAE,MAAM;IACtB,eAAe,EAAE,IAAI;IACrB,kBAAkB,EAAE,IAAI;IACxB,OAAO,EAAE,EAAE;IACX,eAAe,EAAE,GAAG;IACpB,aAAa,EAAE,IAAI;CACpB,CAAC"}
@@ -0,0 +1,39 @@
1
+ # Resend and edit a native user message
2
+
3
+ Native realtime sessions expose `RealtimeOnlineAgent.resend(options?: OnlineAgentResendOptions): Promise<number>`. The options type is exported from the public server and client chat entries. Runner sessions refuse resend, and background sessions do not expose it.
4
+
5
+ ```ts
6
+ await chat.resend(); // Resend the latest saved user message.
7
+ await chat.resend({ messageId: userMessageId });
8
+ await chat.resend({
9
+ messageId: userMessageId,
10
+ input: { text: 'Revised request' },
11
+ });
12
+ const output = await chat.receive();
13
+ ```
14
+
15
+ The target is always a user message. Omitting `messageId` selects the latest user at admission, even when an assistant reply follows it. The return value is the conversation's admitted sequence watermark, not proof that the answer has completed. Consume the existing SDK stream and reconcile business execution state as for an ordinary send.
16
+
17
+ ## Irreversible history replacement
18
+
19
+ One locked admission transaction checks ownership, configuration and idle state, updates the target in place, physically deletes every later message and registers the new execution. The target retains its ID, sequence, creation time, attachment bindings and saved page/UI context; its revision and execution association change. The conversation watermark never decreases, so replacement output receives a sequence above the deleted suffix.
20
+
21
+ `input` may contain only `text`. Omitting it preserves the original parts. Supplying it replaces the original text parts with one part, preserves non-text parts and remaps private file indices. Text is limited to 50,000 Unicode code points. Whitespace-only text requires an original attachment. Current resource and attachment permissions are checked for the retained prefix before destructive writes; the deleted suffix is not prepared as model input.
22
+
23
+ There is no fork, historical version, undo or expected-history/CAS parameter. An idle request from an old tab may delete newer messages it has not seen. Deleting messages does not reverse completed tool or business effects. Regeneration may execute those operations again, and a new approval belongs to the new execution. Deleted messages' physical files are not automatically cleaned up by resend; existing retention and ownership rules remain in force.
24
+
25
+ A rejected admission leaves saved history unchanged. Once admitted, the edited input and deletion remain effective even if generation fails, stops or cannot finalize immediately. Stop, approval and finalization use the existing execution lifecycle, not a second executor.
26
+
27
+ ## HTTP and browser clients
28
+
29
+ `POST /api/agents/conversations/:conversationId/resend` accepts the options object directly and returns the standard AI SDK UI message SSE response. The schema rejects complete browser history, attachment/context replacements, tool output and unknown fields. The route is declared in OpenAPI but deliberately excluded from generated CLI commands.
30
+
31
+ Use `useResendMessage(conversationId)` under the existing identity-keyed `ChatProvider`. UI operations pass an explicit user ID; assistant refresh uses only `metadata.sourceUserMessageId`, including an authorized ID read when that user is outside the current page. Missing linkage hides refresh instead of guessing the nearest user.
32
+
33
+ The coordinator uses the official SDK flows: original resend uses `regenerate({ messageId, body })`, and editing uses `sendMessage({ ...message, messageId, parts }, { body })`. The SDK may truncate its local working array before the POST. Do not substitute approval continuation with `sendMessage(undefined)`, stage another response stream, or use GET stream recovery to start a resend.
34
+
35
+ The hook resolves to `accepted`, `rejected` or `unknown`. A successful POST followed by a failed canonical read remains `accepted` with `needsSync`. Definitive rejection may restore the prior local display and preserve the edit. Unknown admission only rereads authoritative history; it never restores a stale suffix or automatically repeats the POST.
36
+
37
+ `conversation.historyReplaced` announces the accepted target, revision, execution and monotonic watermark. Shared views invalidate older reads, remove the suffix and retain only matching live output. Reconnects, manual synchronization and durable polling also reconcile known message IDs in bounded batches; deletion is inferred only when all requested batches succeed. Subscriber cancellation never invokes business Stop.
38
+
39
+ The installed Registry source supplies public-text copy, inline editing, direct edit/resend submission without a confirmation dialog, independent composer drafts and native/Runner visibility rules. Upgrade that source together with the plugin; an npm package update does not update an application's copied components.
@@ -0,0 +1,76 @@
1
+ # Native OnlineAgent web search
2
+
3
+ Native web search is an opt-in `webSearch` tool for OnlineAgent. It does not enable search for Runner Agents or add an external search service. A search uses the main execution's frozen model-service/model selection and the existing provider connection settings, including its service key and session identity. It never switches model, protocol or provider to make an unsupported request work.
4
+
5
+ ## Enable and configure
6
+
7
+ Search is off by default. Select a configured chat model on an admitted provider in the native chat composer before enabling **Search the web**. A new conversation sends its selected model and `config.webSearch` in the first-create snapshot. For an existing conversation, the control uses the original idle `PATCH /api/agents/conversations/{conversationId}/config` endpoint; the response, not an optimistic local preference, is authoritative. Running, approval, pending configuration and unknown-write reconciliation disable configuration changes. An unknown configuration write is reconciled by reading, never automatically repeating it.
8
+
9
+ Application providers can also include `webSearch: true` in their normal OnlineAgent configuration layers or explicit session options. The internal `effectiveTools(config)` helper includes the versioned built-in only when enabled; applications must not register another tool named `webSearch`. Effective configuration snapshots and public tools use this same selection. Scopes, business tools, variables and permissions keep their existing contracts.
10
+
11
+ The public model catalog exposes derived `capabilities.webSearch`. This field is code-owned and is not an editable model-service setting. Both admission and actual execution check the provider admission policy, model kind and current official endpoint without a model-name allowlist. Explicitly enabling an unsupported selection fails without partially changing the configuration. Switching an already-enabled conversation to a model on an unsupported provider or endpoint clears the inherited flag; it does not remove unrelated business tools or configuration.
12
+
13
+ ## Admission and evidence policy
14
+
15
+ OpenAI, Anthropic, Google and Alibaba use provider-level admission for all configured chat models, without model-name allowlists. Admission makes the control available; it does not establish model compatibility or actual search execution. Official endpoint checks reject embedded credentials, query strings, fragments, proxy paths and custom/compatible hosts. Alibaba additionally admits the documented Beijing, Singapore and Hong Kong legacy and workspace-specific official hosts on `/compatible-mode/v1`. The common SDK is `ai@7.0.128`; the versions below are the locked versions tested, not promises about vendor availability.
16
+
17
+ | Provider / model policy | Official endpoint | Locked SDK / native tool | Requested search-only reasoning options | Required execution and source evidence |
18
+ | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
19
+ | OpenAI / all configured chat models | `https://api.openai.com/v1` | `@ai-sdk/openai@4.0.84`; Responses `tools.webSearch({ externalWebAccess: true })`; forced `web_search`, `maxToolCalls: 1`, `includeWebSearchSources: true` | `reasoningEffort: 'none'` | A completed `web_search_call` with `action.type === 'search'`; URL sources in `action.sources` and the SDK's structured sources. Text or citations without completed search do not qualify. |
20
+ | Anthropic / all configured chat models | `https://api.anthropic.com/v1` | `@ai-sdk/anthropic@4.0.72`; `tools.webSearch_20250305({ maxUses: 1 })`; forced `web_search`, `disableParallelToolUse: true` | `thinking: { type: 'disabled' }` | Matching `server_tool_use` and `web_search_tool_result` with a result array; URLs from `web_search_result`. Unrelated text citations do not qualify. |
21
+ | Google / all configured chat models | `https://generativelanguage.googleapis.com/v1beta` | `@ai-sdk/google@4.0.88`; `tools.googleSearch({})`, automatic tool choice | `thinkingConfig: { thinkingBudget: 0, includeThoughts: false }` | Nonempty `groundingMetadata.webSearchQueries` and safe `groundingChunks[].web.uri` sources. Grounding chunks without executed queries do not qualify. Google may choose not to search; that returns `noEvidence`, not fabricated success. |
22
+ | Alibaba / all configured chat models | Official DashScope regional/workspace `/compatible-mode/v1` endpoints | `@ai-sdk/alibaba@2.0.61`; Chat Completions with instance-scoped fetch adding `enable_search: true` and `search_options.forced_search: true` | `enableThinking: false`, `preserveThinking: false` | An answer without search evidence returns `status: 'unverified'`, findings and `sources: []`. This is not verified search execution or freshness. An empty answer returns `noEvidence`. |
23
+ | DeepSeek, Moonshot, Cohere, Ollama, OpenAI-compatible, OpenCode, custom endpoints and providers without a search adapter | Not admitted | No search adapter | Not applicable | Always `webSearch: false`; ordinary model calls remain available under their existing rules. |
24
+
25
+ OpenAI's native search uses Responses even when ordinary main generation uses Chat Completions. This is an explicit provider adapter for the same exact model and service, not a fallback. Search-only options do not modify global `reasoningOf` or the main conversation's reasoning configuration. All adapters use `maxRetries: 0`.
26
+
27
+ Provider-level admission without model restrictions was explicitly requested by the user. The original three adapters retain their executed-search and safe-source requirement. Alibaba alone permits source-free text under the explicitly approved `unverified` policy, because compatible Chat Completions does not return independently verifiable search evidence. The instance-scoped fetch sets only the SDK request's `enable_search` and `search_options.forced_search` options, preserves headers and cancellation, and never rewrites the endpoint, selected model, credential or protocol. Ordinary Alibaba generation remains unchanged. All incompatible model/options responses fail safely without fallback. The locked OpenAI SDK can warn and omit reasoningEffort for IDs it classifies as unknown/non-reasoning; this is not a vendor capability statement. Alibaba uses its own SDK-typed thinking-off options rather than the OpenAI codec.
28
+
29
+ ### Official references and verification boundary
30
+
31
+ - OpenAI: [Web search](https://developers.openai.com/api/docs/guides/tools-web-search) and [GPT-5.4](https://developers.openai.com/api/docs/models/gpt-5.4). The guide describes lower search quality with GPT-5.4's `none` reasoning setting; the implementation does not substitute a different reasoning level.
32
+ - Anthropic: [Web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool), [extended thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking), and [Claude Sonnet 4.6](https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-6).
33
+ - Google: [Grounding with Google Search](https://ai.google.dev/gemini-api/docs/google-search) and [thinking configuration](https://docs.cloud.google.com/vertex-ai/generative-ai/docs/thinking). Gemini 2.5 Flash permits a zero thinking budget; that conclusion must not be generalized to Gemini 2.5 Pro.
34
+ - Alibaba: [Web search](https://www.alibabacloud.com/help/en/model-studio/web-search) and [forced web search](https://help.aliyun.com/zh/model-studio/web-search#6f83c89f7dw0v). The compatible Chat Completions surface supports `enable_search` and `search_options.forced_search` but does not provide search sources. `enable_search` alone allows the model to decide whether to search; the isolated child also sets `forced_search: true`. Sending both options is still not independent proof of vendor search execution or freshness, so source-free answers remain unverified.
35
+
36
+ When an Alibaba service leaves its base URL empty, requests explicitly use the application catalog's Beijing endpoint (`https://dashscope.aliyuncs.com/compatible-mode/v1`), matching the editor rather than the SDK's implicit Singapore default. An explicitly configured regional/workspace endpoint is preserved. Use a credential valid for that region; an authentication failure in the main model call occurs before the search tool and is not a source-evidence failure.
37
+
38
+ Verification consists of official documentation, locked SDK types, compilation, and actual SDK serialization/parsing against loopback HTTP fixtures. No real provider key or paid model call was used. This does not establish vendor-account availability, regional availability, production attribution/display compliance, live cancellation settlement, billing, response quality or successful live search. Before enabling a real service, review its current account, model, search, attribution and display requirements, then run a separately authorized minimal live acceptance case. Do not describe fixture success as vendor acceptance.
39
+
40
+ ## Isolation and result safety
41
+
42
+ Each query creates an isolated model call containing only the query and fixed retrieval instructions. Main system prompts, history, attachments, page/UI context, business tools, sandbox, knowledge, entry credentials and business results are not copied into that call. The model may deliberately include information in its query; this is not a data-loss-prevention boundary, and users must not submit sensitive queries to an external provider.
43
+
44
+ ### Retrieval prompts and query planning
45
+
46
+ All four providers share `WEB_SEARCH_RETRIEVAL_INSTRUCTIONS` in `server/online/search/prompts.ts`. The child is a retrieval assistant for another model, not the final user-facing author: return 1–6 concise factual bullets, prefer relevant authoritative evidence, distinguish forecast/event dates from publication dates, and name specific gaps without generic website recommendations or unsupported real-time claims. It does not generate a separate Sources section; structured sources still come from provider evidence, never from parsing model-authored URLs. Native citations may be preserved, but prompt wording cannot establish search execution or freshness.
47
+
48
+ `WEB_SEARCH_TOOL_DESCRIPTION` separately guides the main model to batch specific, non-overlapping queries within the four-query call limit and avoid similar repeated searches unless a critical fact remains missing. It retains the untrusted-output and unverified-result warnings. Reference time and location are used only when explicitly available; this change does not inject a date, timezone, location or additional parent context. Provider activation, evidence classification, budgets, retries and cancellation remain unchanged.
49
+
50
+ A result contains `results[]`: verified `{ query, status: 'success', findings, sources: [{ title, url }] }`, Alibaba-only `{ query, status: 'unverified', findings, sources: [] }`, or `{ query, status: 'failed', reason }`. Public reasons are `timeout`, `unavailable` and `noEvidence`. Generated text alone never counts as verified retrieval. Unverified text is retained for the main model with an explicit instruction to disclose that search execution, freshness and sources are unconfirmed; the card omits the visible warning and shows no source list, without changing the model-facing warning. Empty answers fail. Raw response bodies, provider metadata, HTML and exception text do not enter public results/history. Findings are untrusted text, not instructions or executable HTML. Verified sources remain bounded, deduplicated HTTP(S) URLs without credentials, with external links using `noopener noreferrer`. Public normalization preserves the unverified discriminator and rejects invented sources attached to it.
51
+
52
+ | Limit | Value |
53
+ | -------------------------------------- | ------------------------------------ |
54
+ | Queries per tool call / main execution | 4 / 8 |
55
+ | Shared concurrent queries | 2 |
56
+ | Tool deadline / per-query deadline | 60 / 30 seconds |
57
+ | Query / findings | 1,000 / 6,000 Unicode code points |
58
+ | Sources per query / title / URL | 10 / 300 / 4,096 Unicode code points |
59
+
60
+ Reservations occur synchronously before asynchronous work. Genuine approval continuations restore the original call identity and queries, so approving or denying an unrelated business tool does not reset the eight-query budget or repeat completed searches. A changed or duplicate restored search call is rejected. Main cancellation reaches active SDK HTTP requests and prevents queued requests from starting; workers settle before execution cleanup. Query timeout retains other successes. Authentication/configuration failures stop later queued work instead of retrying it. Text usage is recorded through `ModelUsageRecorder` with source `agents.webSearch`; a recording failure does not resend a completed model request. Provider-native search charges may not be represented in text-token usage.
61
+
62
+ ## Chat UI and installed source
63
+
64
+ The Registry and Examples default card render the original `tool-webSearch` part once as a full-width segmented list. Each query owns a four-line fixed-height findings preview with native ellipsis and independent Expand/Collapse controls; collapsed previews fold whitespace to avoid an ellipsis on an empty line, expansion restores the original paragraph breaks, and short findings keep the preview height without a redundant button, and safe source links remain visible. Only an actively streaming unfinished search displays a gentle pulse on the existing globe icon before the card title, with no additional loading icon, distinct from the conversation's spinning indicator. Pending findings use four thicker skeleton bars (100% / 95% / 100% / 90%) distributed across the same four-line preview height, with a 2.2-second themed sweep. The card supplies a React-deduplicated, scoped stylesheet; it does not modify the shared Skeleton primitive or require consumer CSS configuration. Reduced-motion preferences disable the sweep and globe animation. Verification badges and the source-free disclaimer are omitted from the card, while the persisted evidence discriminator and model-output warning remain unchanged. Historical unfinished parts are not shown as busy, and expanding or reloading history never executes tools. An application renderer named `webSearch` takes precedence over the default; inherited renderers, approval cards and application form proposals keep their existing behavior. Do not replace the SDK message owner, add a parallel message store or reconstruct synthetic search messages.
65
+
66
+ The new source imports `@nocobase/app-plugin-agents/shared/web-search` and `@nocobase/app-plugin-agents/shared/web-search-results`; both must exist in source and published `dist` export maps. Registry deployment must wait for the matching Agents release. The Registry floor in this change is `^1.0.0-beta.21`, the next planned feature-bearing prerelease after the rebased beta.20 baseline; verify the actual release contains these exports before publishing the Registry, and adjust that floor if release sequencing changes. Local acceptance uses the current unversioned feature tarball, not a claim that this future package is already published. Do not upgrade every unrelated dependency when raising this floor.
67
+
68
+ When upgrading installed source, merge only the toggle, card, translations, imports and registration changes. Preserve application-owned renderers, form proposals, conversation actions, input availability, creation recovery, retry gating, history refresh, pending presentation and locale customizations. Examples installs the chat; Default does not, so Default needs no chat-source copy. Neither template gains a new global layout or default search policy.
69
+
70
+ ## Offline regression and optional live acceptance
71
+
72
+ Focused backend coverage is in `tests/web-search.test.ts`, `tests/web-search-alibaba.test.ts`, `tests/web-search-providers.test.ts`, `tests/search-budget.test.ts`, `tests/search-capabilities.test.ts` and `tests/web-search-package.test.ts`. It covers real SDK serialization, source requirements and the Alibaba exception, ordinary-chat isolation, unknown model admission, safe errors, active/queued cancellation, concurrency, deadlines, budget restoration, usage and Native model/history replay. Registry and Examples component tests verify bilingual source-free rendering without warning labels or fabricated links, independent expansion, overflow-only controls, resize handling and observer cleanup. Existing chat, configuration, business-tool and approval suites remain required.
73
+
74
+ Examples' `tests/fixtures/web-search-browser-server.ts` starts the existing compiled production-acceptance server with a synthetic loopback transport. Its fixture reroutes the configured official provider URL only in the test process and blocks nonlocal network destinations; production endpoint admission is unchanged. `tests/playwright/web-search.test.ts` uses `APP_URL`, `WEBSEARCH_FIXTURE_URL`, `E2E_USERNAME`, `E2E_PASSWORD` and optional `E2E_BROWSER_PATH`/`E2E_SCREENSHOT_DIR`. Run it only against that disposable fixture. It covers desktop/mobile default-off behavior, first-send model snapshot, configuration PATCH, safe expandable results, partial failure, history without another search, and Stop closing active connections without a permanent spinner.
75
+
76
+ For optional live acceptance, obtain explicit permission for the selected provider and spending first. Use one non-sensitive public query on one exact admitted model, capture only safe request settings and source results, confirm no business context leaves the isolated search call, and verify reasoning configuration and cancellation independently. A live request can incur search and token charges even if later cancelled; do not run it automatically or retry an unknown outcome.
@@ -14,6 +14,12 @@ Pre-send model/Agent changes and first-send ID promotion use `selectConversation
14
14
 
15
15
  A fresh composer also clears carried work context: pinned business objects, registered page-object chips, filters, selected page text and checked UI sources. Page/form registrations remain available for subsequent explicit selection; cleared sources are not read or included in the next message. Closing and reopening the same chat, changing the executor and promoting a newly created conversation ID preserve its carried context. Business launchers attach only the intended context atomically with `openChat({ conversationId, draft, pin, uiSources: [{ kind: 'form', id: 'example-request' }] })`; do not call `selectUI` before opening a different conversation because the reset discards the old selection. UI values are still read only when sending, never when opening the chat. A normal new chat may retain its current route identifier, but carries no old object/filter/selection/UI snapshot.
16
16
 
17
+ ## Resend and inline text edits
18
+
19
+ `useResendMessage(conversationId)` shares destructive admission across all views of the existing controller. Pass `{ messageId }` for original resend or `{ messageId, input: { text } }` for editing. Only a native user message may be targeted; assistant refresh passes its exact `metadata.sourceUserMessageId`, not a positional guess. The controller reads an unloaded source by ID before using the official SDK `regenerate` or `sendMessage` edit entry. Background and Runner do not gain regeneration.
20
+
21
+ SDK local truncation before POST is intentional. Durable replacement remains atomic and authoritative: rejection preserves the edit, acceptance closes it even when canonical synchronization fails, and unknown admission never retries POST or restores a stale suffix. `conversation.historyReplaced` invalidates late pages/ID reads; reconnect, manual refresh and the shared durable poll reconcile existing IDs in bounded batches so a missed deletion event cannot resurrect history. Keep the inline editor independent of the bottom composer draft. Edit Send and assistant Regenerate execute directly without a confirmation dialog. Keep pending-state disabling and a synchronous duplicate-submit guard; permanent suffix deletion and possible repeated business effects still apply. See [the full contract](./message-resend.md).
22
+
17
23
  ## Input, admission and recovery
18
24
 
19
25
  The SDK transport uses the host's authenticated `ApiClient.stream`. A POST contains only the current `message: { id, role: "user", parts: [{ type: "text", text }] }`, authorized attachment IDs and optional page context. It never sends the SDK's complete browser history, assistant parts or tool output as input. Uploaded files remain the existing attachment service's responsibility.
@@ -60,7 +66,7 @@ Tool approval buttons use genuine SDK approval parts, binding `messageId`, `exec
60
66
 
61
67
  A visible conversation has one shared durable-history poll, regardless of surfaces: every 5 seconds while running, every 30 seconds while idle, and none while the document is hidden or after the last surface unmounts. This provides independent-notification catch-up when instance-local announcements are missed. Running conversation detail retains the existing 5-second React Query polling; shared keys deduplicate in-flight reads, while staggered observers can each refetch. The SDK stream removes the old one-second text-growth polling.
62
68
 
63
- A forward history poll makes one GET plus each bounded `nextAfter` continuation. Recovery of an interrupted pending tool can add one ID GET before its GET stream. A revision announcement makes one batched ID GET; duplicate revision announcements from multiple surfaces are coalesced. Successful input admission adds history reconciliation and a detail GET; SDK completion/recovery adds detail, forward history and an ID reread for a revisable assistant. A stalled subscription may require a second completion reconciliation after its SDK reader stops. None of these reads creates SQL writes, locks or transactions. Source creation timestamps and public context are derived in the existing projection/snapshot write; no new storage write is introduced.
69
+ A forward history poll makes one GET plus each bounded `nextAfter` continuation. An authoritative refresh additionally rereads the loaded durable message IDs in batches of at most 100; it applies absence only after every batch succeeds. Recovery of an interrupted pending tool can add one ID GET before its GET stream. A revision announcement makes one batched ID GET; duplicate revision announcements from multiple surfaces are coalesced. Successful input admission adds history reconciliation and a detail GET; SDK completion/recovery adds detail, forward history and an ID reread for a revisable assistant. A stalled subscription may require a second completion reconciliation after its SDK reader stops. These synchronization reads create no SQL writes, locks or transactions. Resend itself is an explicit destructive admission transaction and is never an automatic poll or recovery write.
64
70
 
65
71
  Measured warm read requests, excluding the host's authentication and authorization queries, are shown below. The native fixture has one completed reply; the Runner fixture has two saved source blocks and an interleaved notification. These counts describe the extra requests a client makes, not new SQL added inside an existing endpoint. Every sample has zero writes, transactions and explicit locks.
66
72
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@nocobase/app-plugin-agents",
3
3
  "displayName": "Agents",
4
4
  "description": "Agents for NocoBase applications: agents, the model services online agents call, their runs and conversations, and the runners people connect to run them (registration, the long-poll claim, slots, the sweeper, build jobs, and the runner and CLI tarballs the application serves).",
5
- "version": "1.0.0-beta.20",
5
+ "version": "1.0.0-beta.21",
6
6
  "type": "module",
7
7
  "prettier": "@nocobase/dev-config/prettier",
8
8
  "engines": {
@@ -78,6 +78,14 @@
78
78
  "types": "./dist/shared/models.d.ts",
79
79
  "import": "./dist/shared/models.js"
80
80
  },
81
+ "./shared/web-search": {
82
+ "types": "./dist/shared/web-search.d.ts",
83
+ "import": "./dist/shared/web-search.js"
84
+ },
85
+ "./shared/web-search-results": {
86
+ "types": "./dist/shared/web-search-results.d.ts",
87
+ "import": "./dist/shared/web-search-results.js"
88
+ },
81
89
  "./shared/i18n": {
82
90
  "types": "./dist/shared/i18n.d.ts",
83
91
  "import": "./dist/shared/i18n.js"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: nocobase-app-plugin-agents
3
- description: Register the Agents plugin in a NocoBase App and configure it (agents.* in config.yml), contribute subjects, scopes, business actions, agent defaults, the App's own agents, CLI commands and reports through agentsToken, enable the agents discovered from runners, configure OnlineAgentService and OnlineAgentConfigurator for SDK chats and business tools, set up model services for the App's own model calls, connect runners (registration tokens, protocol, upgrade_required), build and serve the CLI and runner tarballs (pnpm nocobase cli build, --runner), or work out why a run, a runner or a model fails.
3
+ description: Register the Agents plugin in a NocoBase App and configure it (agents.* in config.yml), contribute subjects, scopes, business actions, agent defaults, the App's own agents, CLI commands and reports through agentsToken, enable the agents discovered from runners, configure OnlineAgentService and OnlineAgentConfigurator for SDK chats, business tools and native web search, set up model services for the App's own model calls, connect runners (registration tokens, protocol, upgrade_required), build and serve the CLI and runner tarballs (pnpm nocobase cli build, --runner), or work out why a run, a runner or a model fails.
4
4
  metadata:
5
5
  short-description: Agents, runners, model services and their distribution
6
6
  ---
@@ -32,6 +32,7 @@ The package README is the full reference: read it at `node_modules/@nocobase/app
32
32
  | Contribute subjects, scopes, actions, agent defaults, the App's own agents, CLI commands, people, reports, mounts | [Extension points](references/extension-points.md) |
33
33
  | Agents discovered from runners, the model catalog, enabling agents, availability, the App's own agents | [Agents](references/agents.md) |
34
34
  | Configure OnlineAgentService/OnlineAgentConfigurator, register SDK tools, use realtime/background sessions | [OnlineAgent chats and business tools](references/online-agent.md) |
35
+ | Enable native web search, verify platform admission, isolation, budgets and Registry upgrades | `node_modules/@nocobase/app-plugin-agents/docs/native-web-search.md` |
35
36
  | Open JSON UI context, custom SDK tool cards, generic form adapters and safe browser-draft application | [UI context, tool cards and local form drafts](references/online-agent.md#open-ui-context-custom-tool-cards-and-local-form-drafts) |
36
37
  | Model services, the model gateway, vectors | [Model services](references/model-services.md) |
37
38
  | Register runners, how claims and heartbeats work, local policy, protocol and `upgrade_required` | [Runners](references/runners.md) |
@@ -8,7 +8,7 @@ Use `OnlineAgentService` for SDK conversations that call application services an
8
8
 
9
9
  Import the original `agentsToken` from `@nocobase/app-plugin-agents/server/tokens` and resolve `agentsToken.onlineAgent` in an application provider after the plugin has registered. Do not construct a second service for an HTTP entry or user: the application instance coordinates busy, stop and finalization across all facades. `OnlineAgentService` and `OnlineAgentConfigurator` are exported from `@nocobase/app-plugin-agents/server` and `@nocobase/app-plugin-agents/server/online-agent`; the latter also exports the prefixed configuration, tool and stream contracts.
10
10
 
11
- Application dependencies and function parameters use the complete `OnlineAgentService` type. Resolve the shared instance through `agentsToken`; do not introduce another service interface or a method-only type projection. `OnlineAgent` is the common public session interface, with `history(): Promise<AppUIMessage[]>`, session identity, status, cancellation and finalization. `RealtimeOnlineAgent extends OnlineAgent` adds `send`, `receive`, `backend`, required `approve` and `messages`; `BackgroundOnlineAgent extends OnlineAgent` adds `run`, `get` and `try`, without `send` or browser `receive`. The service's `realtime()` and `background()` factories return the corresponding interfaces, implemented by the actual session classes rather than inline facade objects.
11
+ Application dependencies and function parameters use the complete `OnlineAgentService` type. Resolve the shared instance through `agentsToken`; do not introduce another service interface or a method-only type projection. `OnlineAgent` is the common public session interface, with `history(): Promise<AppUIMessage[]>`, session identity, status, cancellation and finalization. `RealtimeOnlineAgent extends OnlineAgent` adds `send`, `receive`, `backend`, required `approve`, `resend` and `messages`; `BackgroundOnlineAgent extends OnlineAgent` adds `run`, `get` and `try`, without `send` or browser `receive`. The service's `realtime()` and `background()` factories return the corresponding interfaces, implemented by the actual session classes rather than inline facade objects.
12
12
 
13
13
  `onlineAgent.configurator` is the `OnlineAgentConfigurator` (the configuration component). Register global layers, named business scopes and versioned tools once at application boot. It resolves prompts, variables, model lists and tool sets; a Scope selects ordinary configuration and grants no business permission.
14
14
 
@@ -62,6 +62,13 @@ Replace `billing-models/chat` with an enabled chat model in the application's ex
62
62
 
63
63
  Layers resolve in order: global registrations, selected named scopes, then explicit options. Providers run sequentially; omitted fields keep their preceding value and arrays replace it, including empty arrays. Prompt functions can compose `context.systemPrompt` and validate variables. The resolved serializable snapshot is saved. Loading an existing session restores it without reevaluating Providers. Idle explicit overrides reevaluate only the named sources requested; running configuration is frozen. `selectedModel` explicitly selects the existing model columns, while a model-list replacement selects its first entry. A prompt-only override retains the selected model.
64
64
 
65
+ ## Native web search
66
+
67
+ `webSearch` is an opt-in ordinary native tool selected through the existing configuration layers, effective-tool snapshot and idle configuration update. The server derives model capabilities, freezes the same main model for isolated retrieval, and rejects unsupported explicit enabling. Do not register a business tool with that reserved name or replace main conversation tools with provider-hosted search tools. Read `node_modules/@nocobase/app-plugin-agents/docs/native-web-search.md` for the provider-level admission and model compatibility, reasoning-off settings, cancellation and execution budgets, safe sources, Registry release gate and offline-versus-live verification boundary. Compatible/custom endpoints and providers without a search adapter remain unsupported; do not improvise a fallback.
68
+
69
+ The four provider adapters share a retrieval-only prompt: return concise facts and specific gaps for the main model, not a final answer or generic website recommendations. The main tool description batches non-overlapping queries and discourages similar repeats. Source records remain provider-evidence-derived, never reconstructed from prose; reference dates and locations are not automatically injected into the child.
70
+ Alibaba is admitted by platform through its existing Chat Completions SDK and an instance-scoped fetch that sets enable_search=true and search_options.forced_search=true only for the isolated search child. Unlike the other three adapters, it may return source-free text as status=unverified with an empty sources list. Disclose that search execution and freshness cannot be verified; never fabricate citations, classify it as verified success, or silently fall back to another model/protocol. Empty answers and provider errors still fail. The shared model/public projection and installed card preserve this distinction.
71
+
65
72
  ## Use a realtime session or a background result
66
73
 
67
74
  Construct `OnlineAgentContext` from the authenticated user and full trusted entry identity, never from browser JSON. The authorization principal must match `user.id`. Business references are untrusted input and still require authorization in tools.
@@ -121,6 +128,7 @@ Use `abort()` for explicit Stop. Native execution waits for terminal persistence
121
128
 
122
129
  Native `POST /api/agents/conversations/:id/messages` accepts only the current SDK user message (`id`, `role: "user"`, text `parts`) plus authorized attachment IDs, page context and optional selected model. It returns a standard SDK UI message stream. Use a `DefaultChatTransport.prepareSendMessagesRequest` mapper; do not submit browser history or trusted metadata. Tool input/output, assistant/system messages and configuration cannot be supplied as history. Creation starts no model. Detail, list, history, config, attachment and Stop retain ownership checks.
123
130
 
131
+ Native realtime sessions can resend or text-edit an existing user message with `chat.resend({ messageId, input: { text } })`; omitted options select the latest saved user. Admission updates that message, permanently deletes its suffix and preserves original files/context. Completed business effects are not undone. Runner refuses this capability and Background does not expose it. Browser UI uses `useResendMessage` and the official SDK regenerate/edit flow, never an automatic retry of unknown admission. See [resend and edit](../../../docs/message-resend.md) for transaction, stream, authorization and recovery contracts.
124
132
  In both native realtime and background modes, `content.original` is the only complete internal UIMessage, with the same ID as its record and raw JSON tool inputs/results, reasoning and provider/approval evidence. Both modes share the same original-message storage and raw tool JSON, not a separate background ModelMessage codec. Public tools require explicit `public.input`/`public.output` projections. All public history reads, including background `history()`, and realtime streams use current projections; current `toModelOutput` rules separately derive internal model input from the original when it is read. Both conversions use detached copies and never execute tools. Missing or failed projections never expose the raw original. No duplicate full message, migration, compatibility fallback or format version is used. SDK `needsApproval` tools require these projections. The approval endpoint accepts only `{ messageId, executionId, approval: { id, approved, reason? } }` and resumes from trusted persisted history. Waiting approvals, denials and outcomes update the same message with increasing metadata revision; use message revision notifications and `GET messages?ids=...` to reread changes. Approval retains its owner/execution/message binding and does not bypass active execution occupancy. Background structured execution stays separate.
125
133
 
126
134
  Native public history and realtime streams include actual model-provided SDK reasoning, including previously saved parts. Expose only `type`/`text`/optional `state` in history and the SDK routing fields (`type`, `id`, and `delta` for reasoning deltas) in the stream; never expose provider metadata or signatures. Installed Registry/Examples render nonempty parts in order with sanitized Markdown, automatically open only the active streaming part and otherwise collapse by default. A user's manual choice wins for that mounted message/part across incremental updates, part end and history revisions. Reasoning counts as visible output, but does not become answer text, a search/copy value or an execution outcome. Models and Runner responses without genuine reasoning remain ordinary text-only chat.