@superblocksteam/vite-plugin-file-sync 2.0.43-next.9 → 2.0.49

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 (204) hide show
  1. package/dist/ai-service/agent/subagents/apis/api-executor.d.ts +57 -0
  2. package/dist/ai-service/agent/subagents/apis/api-executor.d.ts.map +1 -0
  3. package/dist/ai-service/agent/subagents/apis/api-executor.js +284 -0
  4. package/dist/ai-service/agent/subagents/apis/api-executor.js.map +1 -0
  5. package/dist/ai-service/agent/subagents/apis/context.d.ts +12 -0
  6. package/dist/ai-service/agent/subagents/apis/context.d.ts.map +1 -0
  7. package/dist/ai-service/agent/subagents/apis/context.js +18 -0
  8. package/dist/ai-service/agent/subagents/apis/context.js.map +1 -0
  9. package/dist/ai-service/agent/subagents/apis/generate-api-source.d.ts +37 -31
  10. package/dist/ai-service/agent/subagents/apis/generate-api-source.d.ts.map +1 -1
  11. package/dist/ai-service/agent/subagents/apis/generate-api-source.js +355 -479
  12. package/dist/ai-service/agent/subagents/apis/generate-api-source.js.map +1 -1
  13. package/dist/ai-service/agent/subagents/apis/state.d.ts +40 -0
  14. package/dist/ai-service/agent/subagents/apis/state.d.ts.map +1 -0
  15. package/dist/ai-service/agent/subagents/apis/state.js +25 -0
  16. package/dist/ai-service/agent/subagents/apis/state.js.map +1 -0
  17. package/dist/ai-service/agent/subagents/apis/types.d.ts +5 -0
  18. package/dist/ai-service/agent/subagents/apis/types.d.ts.map +1 -0
  19. package/dist/ai-service/agent/subagents/apis/types.js +2 -0
  20. package/dist/ai-service/agent/subagents/apis/types.js.map +1 -0
  21. package/dist/ai-service/agent/tool-message-utils.d.ts.map +1 -1
  22. package/dist/ai-service/agent/tool-message-utils.js +8 -24
  23. package/dist/ai-service/agent/tool-message-utils.js.map +1 -1
  24. package/dist/ai-service/agent/tools/apis/build-api.d.ts +20 -0
  25. package/dist/ai-service/agent/tools/apis/build-api.d.ts.map +1 -0
  26. package/dist/ai-service/agent/tools/apis/build-api.js +170 -0
  27. package/dist/ai-service/agent/tools/apis/build-api.js.map +1 -0
  28. package/dist/ai-service/agent/tools/apis/finalize-api.d.ts +15 -0
  29. package/dist/ai-service/agent/tools/apis/finalize-api.d.ts.map +1 -0
  30. package/dist/ai-service/agent/tools/apis/finalize-api.js +103 -0
  31. package/dist/ai-service/agent/tools/apis/finalize-api.js.map +1 -0
  32. package/dist/ai-service/agent/tools/build-finalize.d.ts +1 -22
  33. package/dist/ai-service/agent/tools/build-finalize.d.ts.map +1 -1
  34. package/dist/ai-service/agent/tools/build-finalize.js +27 -18
  35. package/dist/ai-service/agent/tools/build-finalize.js.map +1 -1
  36. package/dist/ai-service/agent/tools/integrations/execute-request.d.ts +1 -1
  37. package/dist/ai-service/agent/tools/integrations/execute-request.d.ts.map +1 -1
  38. package/dist/ai-service/agent/tools/integrations/index.d.ts +1 -1
  39. package/dist/ai-service/agent/tools/integrations/index.d.ts.map +1 -1
  40. package/dist/ai-service/agent/tools/integrations/index.js +1 -1
  41. package/dist/ai-service/agent/tools/integrations/index.js.map +1 -1
  42. package/dist/ai-service/agent/tools/integrations/internal.d.ts +1 -1
  43. package/dist/ai-service/agent/tools/integrations/internal.d.ts.map +1 -1
  44. package/dist/ai-service/agent/tools/integrations/internal.js +12 -1
  45. package/dist/ai-service/agent/tools/integrations/internal.js.map +1 -1
  46. package/dist/ai-service/agent/tools/integrations/metadata.d.ts +1 -1
  47. package/dist/ai-service/agent/tools/integrations/metadata.d.ts.map +1 -1
  48. package/dist/ai-service/agent/tools/integrations/metadata.js +1 -1
  49. package/dist/ai-service/agent/tools/integrations/metadata.js.map +1 -1
  50. package/dist/ai-service/agent/tools/integrations/run-code.d.ts +1 -1
  51. package/dist/ai-service/agent/tools/integrations/run-code.d.ts.map +1 -1
  52. package/dist/ai-service/agent/tools/study-current-app-state.d.ts +1 -0
  53. package/dist/ai-service/agent/tools/study-current-app-state.d.ts.map +1 -1
  54. package/dist/ai-service/agent/tools2/access-control.d.ts.map +1 -1
  55. package/dist/ai-service/agent/tools2/access-control.js +5 -2
  56. package/dist/ai-service/agent/tools2/access-control.js.map +1 -1
  57. package/dist/ai-service/agent/tools2/registry.d.ts +2 -1
  58. package/dist/ai-service/agent/tools2/registry.d.ts.map +1 -1
  59. package/dist/ai-service/agent/tools2/registry.js +4 -4
  60. package/dist/ai-service/agent/tools2/registry.js.map +1 -1
  61. package/dist/ai-service/agent/tools2/types.d.ts +17 -3
  62. package/dist/ai-service/agent/tools2/types.d.ts.map +1 -1
  63. package/dist/ai-service/agent/tools2/types.js +21 -0
  64. package/dist/ai-service/agent/tools2/types.js.map +1 -1
  65. package/dist/ai-service/agent/utils.d.ts +1 -0
  66. package/dist/ai-service/agent/utils.d.ts.map +1 -1
  67. package/dist/ai-service/agent/utils.js +1 -0
  68. package/dist/ai-service/agent/utils.js.map +1 -1
  69. package/dist/ai-service/integrations/metadata/database.d.ts.map +1 -1
  70. package/dist/ai-service/integrations/metadata/database.js +61 -20
  71. package/dist/ai-service/integrations/metadata/database.js.map +1 -1
  72. package/dist/ai-service/integrations/metadata/databricks.d.ts.map +1 -1
  73. package/dist/ai-service/integrations/metadata/databricks.js +5 -5
  74. package/dist/ai-service/integrations/metadata/databricks.js.map +1 -1
  75. package/dist/ai-service/integrations/metadata/graphql-based.d.ts +2 -0
  76. package/dist/ai-service/integrations/metadata/graphql-based.d.ts.map +1 -1
  77. package/dist/ai-service/integrations/metadata/graphql-based.js +95 -14
  78. package/dist/ai-service/integrations/metadata/graphql-based.js.map +1 -1
  79. package/dist/ai-service/integrations/metadata/llm-utils.d.ts +24 -0
  80. package/dist/ai-service/integrations/metadata/llm-utils.d.ts.map +1 -0
  81. package/dist/ai-service/integrations/metadata/llm-utils.js +45 -0
  82. package/dist/ai-service/integrations/metadata/llm-utils.js.map +1 -0
  83. package/dist/ai-service/integrations/store.d.ts +5 -5
  84. package/dist/ai-service/integrations/store.d.ts.map +1 -1
  85. package/dist/ai-service/integrations/store.js +52 -53
  86. package/dist/ai-service/integrations/store.js.map +1 -1
  87. package/dist/ai-service/llm/context/constants.d.ts +7 -6
  88. package/dist/ai-service/llm/context/constants.d.ts.map +1 -1
  89. package/dist/ai-service/llm/context/constants.js +7 -6
  90. package/dist/ai-service/llm/context/constants.js.map +1 -1
  91. package/dist/ai-service/llm/context/context-handle.d.ts +106 -0
  92. package/dist/ai-service/llm/context/context-handle.d.ts.map +1 -0
  93. package/dist/ai-service/llm/context/context-handle.js +134 -0
  94. package/dist/ai-service/llm/context/context-handle.js.map +1 -0
  95. package/dist/ai-service/llm/context/context-lock.d.ts +144 -0
  96. package/dist/ai-service/llm/context/context-lock.d.ts.map +1 -0
  97. package/dist/ai-service/llm/context/context-lock.js +221 -0
  98. package/dist/ai-service/llm/context/context-lock.js.map +1 -0
  99. package/dist/ai-service/llm/context/context.d.ts +18 -19
  100. package/dist/ai-service/llm/context/context.d.ts.map +1 -1
  101. package/dist/ai-service/llm/context/context.js +78 -129
  102. package/dist/ai-service/llm/context/context.js.map +1 -1
  103. package/dist/ai-service/llm/context/index.d.ts +4 -0
  104. package/dist/ai-service/llm/context/index.d.ts.map +1 -1
  105. package/dist/ai-service/llm/context/index.js +5 -0
  106. package/dist/ai-service/llm/context/index.js.map +1 -1
  107. package/dist/ai-service/llm/context/internal-types.d.ts +0 -2
  108. package/dist/ai-service/llm/context/internal-types.d.ts.map +1 -1
  109. package/dist/ai-service/llm/context/internal-types.js.map +1 -1
  110. package/dist/ai-service/llm/context/levels/l1.d.ts.map +1 -1
  111. package/dist/ai-service/llm/context/levels/l1.js +3 -5
  112. package/dist/ai-service/llm/context/levels/l1.js.map +1 -1
  113. package/dist/ai-service/llm/context/manager.d.ts +60 -11
  114. package/dist/ai-service/llm/context/manager.d.ts.map +1 -1
  115. package/dist/ai-service/llm/context/manager.js +113 -37
  116. package/dist/ai-service/llm/context/manager.js.map +1 -1
  117. package/dist/ai-service/llm/context/utils/content-compaction.d.ts +2 -2
  118. package/dist/ai-service/llm/context/utils/content-compaction.d.ts.map +1 -1
  119. package/dist/ai-service/llm/context/utils/content-compaction.js +6 -3
  120. package/dist/ai-service/llm/context/utils/content-compaction.js.map +1 -1
  121. package/dist/ai-service/llm/context/utils/index.d.ts +1 -1
  122. package/dist/ai-service/llm/context/utils/index.d.ts.map +1 -1
  123. package/dist/ai-service/llm/context/utils/index.js +1 -1
  124. package/dist/ai-service/llm/context/utils/index.js.map +1 -1
  125. package/dist/ai-service/llm/context/utils/message-utils.d.ts +17 -7
  126. package/dist/ai-service/llm/context/utils/message-utils.d.ts.map +1 -1
  127. package/dist/ai-service/llm/context/utils/message-utils.js +31 -18
  128. package/dist/ai-service/llm/context/utils/message-utils.js.map +1 -1
  129. package/dist/ai-service/llmobs/helpers.d.ts +9 -2
  130. package/dist/ai-service/llmobs/helpers.d.ts.map +1 -1
  131. package/dist/ai-service/llmobs/helpers.js +17 -4
  132. package/dist/ai-service/llmobs/helpers.js.map +1 -1
  133. package/dist/ai-service/llmobs/middleware/retry.d.ts +51 -0
  134. package/dist/ai-service/llmobs/middleware/retry.d.ts.map +1 -0
  135. package/dist/ai-service/llmobs/middleware/retry.js +147 -0
  136. package/dist/ai-service/llmobs/middleware/retry.js.map +1 -0
  137. package/dist/ai-service/llmobs/middleware/stream-text.d.ts.map +1 -1
  138. package/dist/ai-service/llmobs/middleware/stream-text.js +1 -0
  139. package/dist/ai-service/llmobs/middleware/stream-text.js.map +1 -1
  140. package/dist/ai-service/llmobs/tracer.d.ts +4 -0
  141. package/dist/ai-service/llmobs/tracer.d.ts.map +1 -1
  142. package/dist/ai-service/llmobs/tracer.js +11 -0
  143. package/dist/ai-service/llmobs/tracer.js.map +1 -1
  144. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/ButtonPropsDocs.js +1 -1
  145. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/CheckboxPropsDocs.js +1 -1
  146. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/ColumnPropsDocs.js +1 -1
  147. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/ContainerPropsDocs.js +1 -1
  148. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/DatePickerPropsDocs.js +1 -1
  149. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/DropdownPropsDocs.js +1 -1
  150. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/IconPropsDocs.js +1 -1
  151. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/ImagePropsDocs.js +1 -1
  152. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/InputPropsDocs.js +1 -1
  153. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/ModalPropsDocs.js +1 -1
  154. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/PagePropsDocs.js +1 -1
  155. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/SectionPropsDocs.js +1 -1
  156. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/SlideoutPropsDocs.js +1 -1
  157. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/SwitchPropsDocs.js +1 -1
  158. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/TablePropsDocs.js +1 -1
  159. package/dist/ai-service/prompt-builder-service/static-fragments/library-components/TextPropsDocs.js +1 -1
  160. package/dist/ai-service/prompt-builder-service/static-fragments/library-typedefs/Dim.js +1 -1
  161. package/dist/ai-service/prompt-builder-service/static-fragments/library-typedefs/EventFlow.js +1 -1
  162. package/dist/ai-service/prompt-builder-service/static-fragments/library-typedefs/TextStyleWithVariant.js +1 -1
  163. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/full-examples.js +1 -1
  164. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-api.js +1 -1
  165. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-components-rules.js +1 -1
  166. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-custom-components.js +1 -1
  167. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-data-filtering.js +1 -1
  168. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-event-flow.js +1 -1
  169. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-forms.js +1 -1
  170. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-layouts.js +1 -1
  171. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-page.js +1 -1
  172. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-rbac.js +1 -1
  173. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-routes.js +1 -1
  174. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-state.js +1 -1
  175. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/superblocks-theming-chakra-new.js +1 -1
  176. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/system-base.js +1 -1
  177. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/system-incremental.js +1 -1
  178. package/dist/ai-service/prompt-builder-service/static-fragments/platform-parts/system-specific-edit.js +1 -1
  179. package/dist/ai-service/state-machine/clark-fsm.d.ts +2 -0
  180. package/dist/ai-service/state-machine/clark-fsm.d.ts.map +1 -1
  181. package/dist/ai-service/state-machine/clark-fsm.js.map +1 -1
  182. package/dist/ai-service/state-machine/handlers/llm-generating.d.ts.map +1 -1
  183. package/dist/ai-service/state-machine/handlers/llm-generating.js +363 -336
  184. package/dist/ai-service/state-machine/handlers/llm-generating.js.map +1 -1
  185. package/dist/ai-service/util/retry-on-timeout.d.ts +93 -0
  186. package/dist/ai-service/util/retry-on-timeout.d.ts.map +1 -0
  187. package/dist/ai-service/util/retry-on-timeout.js +153 -0
  188. package/dist/ai-service/util/retry-on-timeout.js.map +1 -0
  189. package/dist/ai-service/util/stop-condition.d.ts +4 -1
  190. package/dist/ai-service/util/stop-condition.d.ts.map +1 -1
  191. package/dist/ai-service/util/stop-condition.js +14 -2
  192. package/dist/ai-service/util/stop-condition.js.map +1 -1
  193. package/dist/sync-service/download.d.ts.map +1 -1
  194. package/dist/sync-service/download.js +28 -7
  195. package/dist/sync-service/download.js.map +1 -1
  196. package/dist/util/logger.d.ts +13 -0
  197. package/dist/util/logger.d.ts.map +1 -1
  198. package/dist/util/logger.js +21 -0
  199. package/dist/util/logger.js.map +1 -1
  200. package/package.json +11 -9
  201. package/dist/ai-service/llm/context/logger.d.ts +0 -17
  202. package/dist/ai-service/llm/context/logger.d.ts.map +0 -1
  203. package/dist/ai-service/llm/context/logger.js +0 -26
  204. package/dist/ai-service/llm/context/logger.js.map +0 -1
@@ -1,198 +1,35 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { hasToolCall, smoothStream, tool, } from "ai";
3
+ import { smoothStream } from "ai";
4
4
  import z from "zod";
5
- import { getErrorMeta, getLogger } from "../../../../util/logger.js";
5
+ import { getErrorMeta, getLogger, getPrefixedLogger, } from "../../../../util/logger.js";
6
6
  import { datasourceSdkClassByType, Paths } from "../../../const.js";
7
7
  import { tracedStreamText } from "../../../llmobs/helpers.js";
8
8
  import { getContextId } from "../../../state-machine/helpers/context-id.js";
9
- import { ApiBuilderTypeScriptError } from "../../../transform/api-builder/shared.js";
10
9
  import { YamlToApiBuilderTransformer } from "../../../transform/api-builder/to-sdk-transformer.js";
11
- import { ApiBuilderToYamlTransformer } from "../../../transform/api-builder/to-yaml-transformer.js";
12
10
  import { applyFileTransformations, renderPath, } from "../../../transform/shared.js";
13
11
  import { processLLMConfig } from "../../../util/llm-config-utils.js";
12
+ import { safeJsonStringify } from "../../../util/safe-stringify.js";
13
+ import { hasToolSuccess } from "../../../util/stop-condition.js";
14
14
  import { getToolCallArguments } from "../../tool-message-utils.js";
15
- import { executeRequestToIntegrationToolFactory, searchIntegrationsToolFactory, } from "../../tools/index.js";
16
- import { ensureScopeFileDraft, getPageEntitiesFiltered, processStreamChunk, } from "../../utils.js";
15
+ import { buildApiToolFactory } from "../../tools/apis/build-api.js";
16
+ import { finalizeApiToolFactory } from "../../tools/apis/finalize-api.js";
17
+ import { executeRequestToIntegrationToolFactory, searchIntegrationsToolFactory, readIntegrationMetadataToolFactory, } from "../../tools/index.js";
18
+ import { getPageEntitiesFiltered, processStreamChunk } from "../../utils.js";
17
19
  import { createToolFactory } from "./../../tools2/types.js";
20
+ import { ApiGenerationContext } from "./context.js";
18
21
  import { selectExamplesByTags, formatSelectedExamples, } from "./example-selector.js";
19
22
  import { VALID_TAGS } from "./examples.js";
20
23
  import { buildApiPrompt } from "./prompt-builder.js";
21
- import { apiStaticAnalysis } from "./static-analysis.js";
24
+ import { ApiGenerationStateMap } from "./state.js";
22
25
  import systemPrompt from "./system-prompt.js";
23
- export const buildApiTools = (input, isNewApi, promptContext, clark, services, scopeUpdateQueue,
24
- // TODO(colin): find a better way to do this
25
- onFinalized) => {
26
- const { apiName, pageName, prompt } = input;
27
- const integrationById = new Map();
28
- let apiYaml;
29
- let buildApiAttempts = 0;
30
- for (const integration of promptContext?.integrations ?? []) {
31
- integrationById.set(integration.id, integration);
32
- }
33
- const readIntegrationMetadata = tool({
34
- description: "Read the metadata for a given integration configuration ID. REQUIRED before using database integrations (PostgreSQL, MySQL, Databricks, Snowflake, etc.) to get the correct schema/table structure. Call this first when working with any integration to avoid making up column names or table structures.",
35
- inputSchema: z.object({ integrationId: z.string() }),
36
- execute: async ({ integrationId }) => {
37
- const integration = integrationById.get(integrationId);
38
- if (!integration) {
39
- throw new Error(`Integration with ID ${integrationId} not found`);
40
- }
41
- const integrationWithOptimizedMetadata = await services.integrationStore.getIntegrationForPromptContext(integration, prompt, clark, services);
42
- return integrationWithOptimizedMetadata?.metadata ?? {};
43
- },
44
- });
45
- const fetchIntegration = tool({
46
- description: "Fetch the integration configuration for a given integration ID",
47
- inputSchema: z.object({ integrationId: z.string() }),
48
- execute: async ({ integrationId }) => {
49
- const integration = await clark.context.peer?.call.aiGetIntegration({
50
- integrationId,
51
- });
52
- if (integration) {
53
- const existing = integrationById.get(integrationId);
54
- const mergedMetadata = {
55
- ...(integration?.metadata ?? {}),
56
- ...(existing?.metadata ?? {}),
57
- };
58
- integrationById.set(integrationId, {
59
- ...integration,
60
- metadata: mergedMetadata,
61
- });
62
- }
63
- return integration;
64
- },
65
- });
66
- const buildApi = tool({
67
- description: "Compile the API source code.",
68
- inputSchema: z.object({
69
- apiSource: z.string().describe("The source code of the API"),
70
- apiName: z.string().describe("The name of the API"),
71
- }),
72
- execute: async ({ apiSource }) => {
73
- buildApiAttempts++;
74
- if (buildApiAttempts > 5) {
75
- throw new Error("Fatal error: Too many attempts to build the API");
76
- }
77
- const generatedArtifact = {
78
- type: "file",
79
- filePath: renderPath(Paths.GeneratedApis, {
80
- pageName,
81
- apiName,
82
- }) + ".ts",
83
- content: apiSource,
84
- };
85
- try {
86
- [apiYaml] = await applyFileTransformations([generatedArtifact], [
87
- new ApiBuilderToYamlTransformer({
88
- templateRenderer: services.templateRenderer,
89
- skipCleanup: true,
90
- }),
91
- ]);
92
- // At this point we know the apiSource is valid TypeScript.
93
- // Now do some static analysis to ensure it's semantically valid
94
- // We do this against the TS source instead of the YAML because
95
- // it's easier to provide better errors to the LLM, which is dealing in TS
96
- const analysisWarnings = apiStaticAnalysis(apiSource);
97
- if (analysisWarnings.length > 0) {
98
- throw new Error(`Static analysis detected potential issues in the API:\n\n${analysisWarnings.join("\n\n")}`);
99
- }
100
- }
101
- catch (error) {
102
- if (error instanceof ApiBuilderTypeScriptError) {
103
- throw new Error(`There were issues compiling the apiSource to TypeScript. Correct the errors below and try again.
104
-
105
- ${error.message.replace(/src\/to-yaml/g, "")}
106
- `);
107
- }
108
- else {
109
- throw error;
110
- }
111
- }
112
- return {
113
- message: "API compiled successfully",
114
- apiName,
115
- pageName,
116
- };
117
- },
118
- });
119
- const finalizeApi = tool({
120
- description: "Finalize. Create a summary of the work completed and a TypeScript interface describing the expected response from the API.",
121
- inputSchema: z.object({
122
- responseInterface: z
123
- .string()
124
- .describe("A TypeScript interface describing the expected response from the API"),
125
- inputInterface: z
126
- .string()
127
- .describe("A TypeScript interface describing the expected input to the API"),
128
- summary: z.string().describe("A summary of the API"),
129
- apiName: z.string().describe("The name of the API"),
130
- givingUpDueToFatalError: z
131
- .boolean()
132
- .optional()
133
- .describe("Whether to give up due to a fatal error"),
134
- }),
135
- execute: async ({ responseInterface, inputInterface, summary, givingUpDueToFatalError, }) => {
136
- if (givingUpDueToFatalError) {
137
- const message = "Giving up due to a fatal error. No API was created.";
138
- onFinalized(message);
139
- return {
140
- message,
141
- apiName,
142
- pageName,
143
- };
144
- }
145
- if (!apiYaml) {
146
- throw new Error("Unexpected state: Was the buildApi tool called successfully?");
147
- }
148
- if (isNewApi) {
149
- // if it's a new API, the scope will be touched
150
- await scopeUpdateQueue.enqueue(async () => {
151
- await ensureScopeFileDraft({
152
- identifier: pageName,
153
- type: "name",
154
- }, services);
155
- await services.draftInterface.createDraftFile(apiYaml.filePath, apiYaml.content);
156
- });
157
- }
158
- else {
159
- await services.draftInterface.createDraftFile(apiYaml.filePath, apiYaml.content);
160
- }
161
- let message = `${summary}
162
-
163
- The API response corresponds to the following TypeScript interface:
164
-
165
- \`\`\`typescript
166
- ${responseInterface}
167
- \`\`\`
168
-
169
- The API input corresponds to the following TypeScript interface:
170
-
171
- \`\`\`typescript
172
- ${inputInterface}
173
- \`\`\`
174
- `;
175
- if (isNewApi) {
176
- message = `${message}
177
-
178
- The ${pageName} scope was updated to include the ${apiName} entity.
179
- `;
180
- }
181
- onFinalized(message);
182
- return {
183
- message: summary,
184
- apiName,
185
- pageName,
186
- };
187
- },
188
- });
26
+ export const buildApiTools = (clark, services, scopeUpdateQueue) => {
189
27
  return {
190
- readIntegrationMetadata,
191
- fetchIntegration,
28
+ readIntegrationMetadata: readIntegrationMetadataToolFactory.create(clark, services),
192
29
  searchIntegrations: searchIntegrationsToolFactory.create(clark, services),
193
30
  executeRequestToIntegration: executeRequestToIntegrationToolFactory.create(clark, services),
194
- buildApi,
195
- finalizeApi,
31
+ buildApi: buildApiToolFactory.create(clark, services),
32
+ finalizeApi: finalizeApiToolFactory.create(clark, services, scopeUpdateQueue),
196
33
  };
197
34
  };
198
35
  export const readApiSource = async (pageName, apiName, pageEntityNames, services) => {
@@ -225,6 +62,7 @@ export const readApiSource = async (pageName, apiName, pageEntityNames, services
225
62
  return sdkArtifact;
226
63
  };
227
64
  export const generateApiSourceSubagentToolFactory = createToolFactory("build_generateApiSource", (clark, services, scopeUpdateQueue, promptContext, logRef) => {
65
+ const logger = getPrefixedLogger("[api-subagent]");
228
66
  const model = services.llmProvider.modelForClassification("broad_edit");
229
67
  const llmConfig = clark.context.llmConfig;
230
68
  return {
@@ -233,8 +71,14 @@ export const generateApiSourceSubagentToolFactory = createToolFactory("build_gen
233
71
  When calling this tool, analyze the user's requirements and provide 2-4 relevant exampleHints tags.
234
72
  `,
235
73
  inputSchema: z.object({
236
- apiName: z.string().describe("The name of the API"),
237
- pageName: z.string().describe("The name of the page"), // this could be an enum from app context
74
+ id: z
75
+ .object({
76
+ apiName: z.string().describe("The name of the API"),
77
+ pageName: z
78
+ .string()
79
+ .describe("The name of the page the API belongs to"), // this could be an enum from app context
80
+ })
81
+ .describe("The reference ID of the API being finalized"),
238
82
  action: z.enum(["create", "edit"]).describe("The action to take"),
239
83
  prompt: z
240
84
  .string()
@@ -245,330 +89,362 @@ When calling this tool, analyze the user's requirements and provide 2-4 relevant
245
89
  .describe(`Optional tags to help select relevant examples to guide the API subagent. Provide 2-4 most relevant tags based on the task requirements.`),
246
90
  }),
247
91
  execute: async (input) => {
248
- const { apiName, pageName, prompt, exampleHints } = input;
249
- let eligibleIntegrations;
250
- let missingIntegrationIds;
251
- const pageEntities = getPageEntitiesFiltered(pageName, ["StateVar", "Component"], services);
252
- const pageEntityNames = pageEntities.map((entity) => entity.name);
253
- const existingSdkApiArtifact = await readApiSource(pageName, apiName, pageEntityNames, services);
254
- if (existingSdkApiArtifact) {
255
- const existingIntegrationIds = services.appContextStore.apiSemanticsByName[apiName]?.blocks
256
- .map((block) => block.id)
257
- .filter((id) => id !== undefined) ?? [];
258
- eligibleIntegrations = existingIntegrationIds
259
- .map((id) => promptContext?.integrations?.find((integration) => integration.id === id))
260
- .filter((x) => x !== undefined);
261
- const promptContextIntegrationIds = new Set(promptContext?.integrations?.map((integration) => integration.id) ??
262
- []);
263
- missingIntegrationIds = existingIntegrationIds.filter((id) => !promptContextIntegrationIds.has(id));
264
- }
265
- else {
266
- eligibleIntegrations = promptContext?.integrations;
267
- }
268
- let finalizedMessage = "";
269
- const tools = buildApiTools(input, !existingSdkApiArtifact, promptContext, clark, services, scopeUpdateQueue, (message) => {
270
- finalizedMessage = message;
271
- });
272
- // Build API prompt using shared helper
273
- const apiPrompt = buildApiPrompt({
274
- apiName,
275
- userPrompt: prompt,
276
- pageEntities: pageEntities.map((e) => ({
277
- name: e.name,
278
- type: e.type,
279
- componentType: e.componentType,
280
- })),
281
- availableIntegrations: eligibleIntegrations?.map((integration) => ({
282
- name: integration.name,
283
- id: integration.id,
284
- type: datasourceSdkClassByType[integration.type] || integration.type,
285
- })),
286
- existingApiSource: existingSdkApiArtifact?.content,
287
- missingIntegrationIds,
288
- isEdit: !!existingSdkApiArtifact,
289
- });
290
- // Select relevant examples based on hints provided by upstream LLM
291
- const selectedExampleMetadata = selectExamplesByTags(exampleHints, {
292
- maxExamples: 6,
293
- includeNegative: true,
294
- maxTokenBudget: 7000,
295
- });
296
- const examplesContent = formatSelectedExamples(selectedExampleMetadata);
297
- // Log selected examples for debugging
298
- if (exampleHints && exampleHints.length > 0) {
299
- logRef.content += `[EXAMPLE SELECTION] Hints: ${exampleHints.join(", ")}\n`;
300
- logRef.content += `[EXAMPLE SELECTION] Selected: ${selectedExampleMetadata.map((e) => e.id).join(", ")}\n\n`;
301
- }
302
- // Set up context for the API subagent
303
- const contextId = getContextId(clark, services);
92
+ const { id, prompt, exampleHints } = input;
93
+ const { pageName, apiName } = id;
94
+ // Set up context for the API subagent with thread-safe ownership
95
+ const subagentName = `api-subagent-${pageName}-${apiName}`;
96
+ const contextId = getContextId(clark, services, subagentName);
304
97
  const contextOptions = clark.context.llmConfig?.contextOptions;
305
- const context = await services.contextManager.getContext(contextId, contextOptions);
306
- const mainSystemPrompt = context.getSystemPrompt();
307
- // Combine system prompt with examples
308
- const fullSystemPrompt = examplesContent
309
- ? `${systemPrompt}\n\n${examplesContent}`
310
- : systemPrompt;
311
- context.setSystemPrompt({
312
- role: "system",
313
- content: fullSystemPrompt,
314
- });
315
- context.startTurn({
316
- role: "user",
317
- content: apiPrompt,
98
+ // Generate a unique owner ID for this subagent execution
99
+ const ownerId = `${subagentName}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
100
+ // Acquire exclusive context access with retries
101
+ const contextHandle = await services.contextManager.acquireContext(contextId, ownerId, {
102
+ contextOptions,
103
+ acquireOptions: {
104
+ waitTimeoutMs: 10000, // Wait up to 10 seconds for context
105
+ lockTimeoutMs: 300000, // Hold lock for up to 5 minutes
106
+ retryIntervalMs: 100,
107
+ },
318
108
  });
319
- // Process LLM configuration
320
- const { providerOptions, headers } = processLLMConfig(llmConfig, 5000, // default budget tokens
321
- `API Subagent (model=${model.modelId})`);
322
- const apiSubagentTrack = "api_subagent";
323
- services.clarkProfiler
324
- .getProfiler()
325
- .createTrack(apiSubagentTrack, "API Subagent", "tool");
326
- return services.clarkProfiler.getProfiler().timeAsync(`generateApiSource:${apiName}`, async () => {
327
- let stepCount = 0;
328
- let thinkingSpanActive = false;
329
- let textSpanActive = false;
330
- const allReasoningDeltas = [];
331
- let currentStepReasoningDeltas = [];
332
- let currentStepTextDeltas = [];
333
- // Step accumulators for accurate token counting
334
- const startTime = new Date().toISOString();
335
- let totalInputTokens = 0;
336
- let totalOutputTokens = 0;
337
- let totalCachedTokens = 0;
338
- let finalizeApiData = {
109
+ try {
110
+ const context = contextHandle.context;
111
+ const mainSystemPrompt = context.getSystemPrompt();
112
+ let eligibleIntegrations;
113
+ let missingIntegrationIds;
114
+ const pageEntities = getPageEntitiesFiltered(pageName, ["StateVar", "Component"], services);
115
+ const pageEntityNames = pageEntities.map((entity) => entity.name);
116
+ const existingSdkApiArtifact = await readApiSource(pageName, apiName, pageEntityNames, services);
117
+ if (existingSdkApiArtifact) {
118
+ const existingIntegrationIds = services.appContextStore.apiSemanticsByName[apiName]?.blocks
119
+ .map((block) => block.id)
120
+ .filter((id) => id !== undefined) ?? [];
121
+ eligibleIntegrations = existingIntegrationIds
122
+ .map((id) => promptContext?.integrations?.find((integration) => integration.id === id))
123
+ .filter((x) => x !== undefined);
124
+ const promptContextIntegrationIds = new Set(promptContext?.integrations?.map((integration) => integration.id) ?? []);
125
+ missingIntegrationIds = existingIntegrationIds.filter((id) => !promptContextIntegrationIds.has(id));
126
+ }
127
+ else {
128
+ eligibleIntegrations = promptContext?.integrations;
129
+ }
130
+ if (!clark.context.apiGenStateMap) {
131
+ logger.info("Initializing API generation state map");
132
+ clark.context.apiGenStateMap = new ApiGenerationStateMap();
133
+ }
134
+ const apiGenStateMap = clark.context.apiGenStateMap;
135
+ apiGenStateMap.set(id, {
136
+ status: "ready",
137
+ isNewApi: !existingSdkApiArtifact,
138
+ });
139
+ logger.info(`API generation started for ${pageName}/${apiName} (existing: ${existingSdkApiArtifact ? "yes" : "no"})`);
140
+ const tools = buildApiTools(clark, services, scopeUpdateQueue);
141
+ // Build API prompt using shared helper
142
+ const apiPrompt = buildApiPrompt({
339
143
  apiName,
340
- responseInterface: "",
341
- inputInterface: "",
342
- summary: "",
343
- };
344
- const response = tracedStreamText({
345
- model,
346
- messages: context.getMessages(),
347
- abortSignal: clark.context.abortController?.signal,
348
- providerOptions,
349
- headers,
350
- experimental_transform: [smoothStream({ chunking: "line" })],
351
- tools,
352
- prepareStep: async (step) => {
353
- context.startStep();
354
- const messages = context.getMessages();
355
- return { ...step, messages };
356
- },
357
- stopWhen: hasToolCall("finalizeApi"),
358
- onChunk: (chunkData) => {
359
- if (chunkData.chunk.type === "reasoning-delta") {
360
- allReasoningDeltas.push(chunkData.chunk.text);
361
- currentStepReasoningDeltas.push(chunkData.chunk.text);
362
- if (!thinkingSpanActive) {
363
- thinkingSpanActive = true;
144
+ userPrompt: prompt,
145
+ pageEntities: pageEntities.map((e) => ({
146
+ name: e.name,
147
+ type: e.type,
148
+ componentType: e.componentType,
149
+ })),
150
+ availableIntegrations: eligibleIntegrations?.map((integration) => ({
151
+ name: integration.name,
152
+ id: integration.id,
153
+ type: datasourceSdkClassByType[integration.type] || integration.type,
154
+ })),
155
+ existingApiSource: existingSdkApiArtifact?.content,
156
+ missingIntegrationIds,
157
+ isEdit: !!existingSdkApiArtifact,
158
+ });
159
+ // Select relevant examples based on hints provided by upstream LLM
160
+ const selectedExampleMetadata = selectExamplesByTags(exampleHints, {
161
+ maxExamples: 6,
162
+ includeNegative: true,
163
+ maxTokenBudget: 7000,
164
+ });
165
+ const examplesContent = formatSelectedExamples(selectedExampleMetadata);
166
+ // Log selected examples for debugging
167
+ if (exampleHints && exampleHints.length > 0) {
168
+ logRef.content += `[EXAMPLE SELECTION] Hints: ${exampleHints.join(", ")}\n`;
169
+ logRef.content += `[EXAMPLE SELECTION] Selected: ${selectedExampleMetadata.map((e) => e.id).join(", ")}\n\n`;
170
+ }
171
+ // Combine system prompt with examples
172
+ const fullSystemPrompt = examplesContent
173
+ ? `${systemPrompt}\n\n${examplesContent}`
174
+ : systemPrompt;
175
+ context.setSystemPrompt({
176
+ role: "system",
177
+ content: fullSystemPrompt,
178
+ });
179
+ context.startTurn({
180
+ role: "user",
181
+ content: apiPrompt,
182
+ });
183
+ // Process LLM configuration
184
+ const { providerOptions, headers } = processLLMConfig(llmConfig, 5000, // default budget tokens
185
+ `API Subagent (model=${model.modelId})`);
186
+ const apiSubagentTrack = "api_subagent";
187
+ services.clarkProfiler
188
+ .getProfiler()
189
+ .createTrack(apiSubagentTrack, "API Subagent", "tool");
190
+ return await services.clarkProfiler.getProfiler().timeAsync(`generateApiSource:${apiName}`, async () => {
191
+ let stepCount = 0;
192
+ let thinkingSpanActive = false;
193
+ let textSpanActive = false;
194
+ const allReasoningDeltas = [];
195
+ let currentStepReasoningDeltas = [];
196
+ let currentStepTextDeltas = [];
197
+ // Step accumulators for accurate token counting
198
+ const startTime = new Date().toISOString();
199
+ let totalInputTokens = 0;
200
+ let totalOutputTokens = 0;
201
+ let totalCachedTokens = 0;
202
+ const response = tracedStreamText({
203
+ model,
204
+ messages: context.getMessages(),
205
+ abortSignal: clark.context.abortController?.signal,
206
+ providerOptions,
207
+ headers,
208
+ experimental_transform: [smoothStream({ chunking: "line" })],
209
+ tools,
210
+ prepareStep: async (step) => {
211
+ context.startStep();
212
+ const messages = context.getMessages();
213
+ return { ...step, messages };
214
+ },
215
+ stopWhen: hasToolSuccess("finalizeApi"),
216
+ onChunk: (chunkData) => {
217
+ if (chunkData.chunk.type === "reasoning-delta") {
218
+ allReasoningDeltas.push(chunkData.chunk.text);
219
+ currentStepReasoningDeltas.push(chunkData.chunk.text);
220
+ if (!thinkingSpanActive) {
221
+ thinkingSpanActive = true;
222
+ services.clarkProfiler
223
+ .getProfiler()
224
+ .startFrame(`API Thinking Step ${stepCount + 1}`, apiSubagentTrack, {
225
+ stepNumber: stepCount + 1,
226
+ chunkType: chunkData.chunk.type,
227
+ apiName,
228
+ });
229
+ }
230
+ }
231
+ if (chunkData.chunk.type === "text-delta") {
232
+ currentStepTextDeltas.push(chunkData.chunk.text);
233
+ if (!textSpanActive) {
234
+ textSpanActive = true;
235
+ services.clarkProfiler
236
+ .getProfiler()
237
+ .startFrame(`API Text Generation Step ${stepCount + 1}`, apiSubagentTrack, {
238
+ stepNumber: stepCount + 1,
239
+ firstTextDelta: chunkData.chunk.text.slice(0, 50) +
240
+ (chunkData.chunk.text.length > 50 ? "..." : ""),
241
+ apiName,
242
+ });
243
+ }
244
+ }
245
+ },
246
+ // Clark often gets the id wrong across tool calls, so we put it in context
247
+ experimental_context: new ApiGenerationContext(id),
248
+ onStepFinish: async (step) => {
249
+ stepCount++;
250
+ const stepTimestamp = new Date().toISOString();
251
+ context.endStep(step.response.messages, step.usage);
252
+ logRef.content += `--- OUTPUT STEP (API subagent) [${stepTimestamp}] ---\n`;
253
+ totalInputTokens += step.usage?.inputTokens ?? 0;
254
+ if (step.reasoning && thinkingSpanActive) {
364
255
  services.clarkProfiler
365
256
  .getProfiler()
366
- .startFrame(`API Thinking Step ${stepCount + 1}`, apiSubagentTrack, {
367
- stepNumber: stepCount + 1,
368
- chunkType: chunkData.chunk.type,
257
+ .updateActiveFrameArgs(apiSubagentTrack, {
258
+ completeReasoningText: currentStepReasoningDeltas.join(" "),
259
+ reasoningLength: step.reasoning.length,
260
+ stepComplete: true,
369
261
  apiName,
370
262
  });
263
+ services.clarkProfiler
264
+ .getProfiler()
265
+ .endFrame(apiSubagentTrack);
266
+ thinkingSpanActive = false;
267
+ currentStepReasoningDeltas = [];
371
268
  }
372
- }
373
- if (chunkData.chunk.type === "text-delta") {
374
- currentStepTextDeltas.push(chunkData.chunk.text);
375
- if (!textSpanActive) {
376
- textSpanActive = true;
269
+ if (step.text && textSpanActive) {
377
270
  services.clarkProfiler
378
271
  .getProfiler()
379
- .startFrame(`API Text Generation Step ${stepCount + 1}`, apiSubagentTrack, {
380
- stepNumber: stepCount + 1,
381
- firstTextDelta: chunkData.chunk.text.slice(0, 50) +
382
- (chunkData.chunk.text.length > 50 ? "..." : ""),
272
+ .updateActiveFrameArgs(apiSubagentTrack, {
273
+ completeTextContent: currentStepTextDeltas.join(""),
274
+ finalText: step.text,
275
+ textLength: step.text.length,
276
+ stepComplete: true,
383
277
  apiName,
384
278
  });
279
+ services.clarkProfiler
280
+ .getProfiler()
281
+ .endFrame(apiSubagentTrack);
282
+ textSpanActive = false;
283
+ currentStepTextDeltas = [];
385
284
  }
386
- }
387
- },
388
- onStepFinish: async (step) => {
389
- stepCount++;
390
- const stepTimestamp = new Date().toISOString();
391
- context.endStep(step.response.messages, step.usage);
392
- logRef.content += `--- OUTPUT STEP (API subagent) [${stepTimestamp}] ---\n`;
393
- totalInputTokens += step.usage?.inputTokens ?? 0;
394
- if (step.reasoning && thinkingSpanActive) {
395
- services.clarkProfiler
396
- .getProfiler()
397
- .updateActiveFrameArgs(apiSubagentTrack, {
398
- completeReasoningText: currentStepReasoningDeltas.join(" "),
399
- reasoningLength: step.reasoning.length,
400
- stepComplete: true,
401
- apiName,
402
- });
403
- services.clarkProfiler
404
- .getProfiler()
405
- .endFrame(apiSubagentTrack);
406
- thinkingSpanActive = false;
407
- currentStepReasoningDeltas = [];
408
- }
409
- if (step.text && textSpanActive) {
410
- services.clarkProfiler
411
- .getProfiler()
412
- .updateActiveFrameArgs(apiSubagentTrack, {
413
- completeTextContent: currentStepTextDeltas.join(""),
414
- finalText: step.text,
415
- textLength: step.text.length,
416
- stepComplete: true,
417
- apiName,
418
- });
419
- services.clarkProfiler
420
- .getProfiler()
421
- .endFrame(apiSubagentTrack);
422
- textSpanActive = false;
423
- currentStepTextDeltas = [];
424
- }
425
- if (step.reasoning) {
426
- const reasoningLines = [
427
- "[REASONING]",
428
- ...step.reasoning.map(({ text }) => text),
429
- "",
430
- ];
431
- logRef.content += reasoningLines.join("\n");
432
- const reasoningText = step.reasoning
433
- .map(({ text }) => text)
434
- .join(" ");
435
- if (reasoningText.trim()) {
436
- void services.chatSessionStore.recordAssistant({
437
- type: "reasoning",
438
- text: reasoningText,
439
- group: `api-${apiName}`,
440
- });
441
- }
442
- }
443
- if (step.toolCalls.length > 0) {
444
- try {
445
- await Promise.all(step.toolCalls.map(async (toolCall) => {
446
- const args = await getToolCallArguments(toolCall.toolName, toolCall.input, clark);
447
- await services.chatSessionStore.recordAssistant({
448
- type: "tool",
449
- tool: toolCall.toolName,
450
- args: args,
285
+ if (step.reasoning) {
286
+ const reasoningLines = [
287
+ "[REASONING]",
288
+ ...step.reasoning.map(({ text }) => text),
289
+ "",
290
+ ];
291
+ logRef.content += reasoningLines.join("\n");
292
+ const reasoningText = step.reasoning
293
+ .map(({ text }) => text)
294
+ .join(" ");
295
+ if (reasoningText.trim()) {
296
+ void services.chatSessionStore.recordAssistant({
297
+ type: "reasoning",
298
+ text: reasoningText,
451
299
  group: `api-${apiName}`,
452
300
  });
453
- }));
454
- }
455
- catch (error) {
456
- getLogger().error("Failed to record subagent tool calls", getErrorMeta(error));
457
- }
458
- }
459
- // Record text messages AFTER tool calls
460
- if (step.text) {
461
- logRef.content += `[ASSISTANT TEXT] ${step.text}\n`;
462
- void services.chatSessionStore.recordAssistant({
463
- type: "text",
464
- text: step.text,
465
- group: `api-${apiName}`,
466
- });
467
- }
468
- // Accumulate token usage for this step
469
- if (step.usage) {
470
- const stepInputTokens = step.usage.inputTokens ?? 0;
471
- const stepOutputTokens = step.usage.outputTokens ?? 0;
472
- const stepCachedTokens = step.usage.cachedInputTokens ?? 0;
473
- // Accumulate tokens across all steps
474
- totalInputTokens += stepInputTokens;
475
- totalOutputTokens += stepOutputTokens;
476
- totalCachedTokens += stepCachedTokens;
477
- }
478
- const finalizeApiResult = step.toolResults.find((r) => r.toolName === "finalizeApi");
479
- if (finalizeApiResult) {
480
- finalizeApiData = finalizeApiResult.input;
481
- }
482
- const toolsCalled = step.content
483
- .filter((c) => c.type === "tool-result")
484
- .map((c) => ({
485
- toolName: c.toolName,
486
- input: JSON.stringify(c.input),
487
- output: JSON.stringify(c.output, null, 2),
488
- }));
489
- if (toolsCalled.length > 0) {
490
- logRef.content += `[TOOLS CALLED]\n`;
491
- toolsCalled.forEach((tool, idx) => {
492
- logRef.content += ` Tool ${idx + 1}: ${tool.toolName}\n`;
493
- logRef.content += ` Input: ${tool.input}\n`;
494
- logRef.content += ` Output: ${tool.output}\n`;
495
- });
496
- toolsCalled.forEach((tool, idx) => {
497
- let parsedInput, parsedOutput;
498
- try {
499
- parsedInput = JSON.parse(tool.input);
500
- }
501
- catch {
502
- parsedInput = tool.input;
503
301
  }
302
+ }
303
+ if (step.toolCalls.length > 0) {
504
304
  try {
505
- parsedOutput = JSON.parse(tool.output);
305
+ await Promise.all(step.toolCalls.map(async (toolCall) => {
306
+ const args = await getToolCallArguments(toolCall.toolName, toolCall.input, clark);
307
+ await services.chatSessionStore.recordAssistant({
308
+ type: "tool",
309
+ tool: toolCall.toolName,
310
+ args: args,
311
+ group: `api-${apiName}`,
312
+ });
313
+ }));
506
314
  }
507
- catch {
508
- parsedOutput = tool.output;
315
+ catch (error) {
316
+ getLogger().error("Failed to record subagent tool calls", getErrorMeta(error));
509
317
  }
510
- services.clarkProfiler
511
- .getProfiler()
512
- .addInstantEvent(`API Tool Call: ${tool.toolName}`, apiSubagentTrack, {
513
- step: stepCount,
514
- toolIndex: idx + 1,
515
- toolName: tool.toolName,
516
- input: parsedInput,
517
- output: parsedOutput,
518
- inputSize: tool.input.length,
519
- outputSize: tool.output.length,
520
- apiName,
318
+ }
319
+ // Record text messages AFTER tool calls
320
+ if (step.text) {
321
+ logRef.content += `[ASSISTANT TEXT] ${step.text}\n`;
322
+ void services.chatSessionStore.recordAssistant({
323
+ type: "text",
324
+ text: step.text,
325
+ group: `api-${apiName}`,
326
+ });
327
+ }
328
+ // Accumulate token usage for this step
329
+ if (step.usage) {
330
+ const stepInputTokens = step.usage.inputTokens ?? 0;
331
+ const stepOutputTokens = step.usage.outputTokens ?? 0;
332
+ const stepCachedTokens = step.usage.cachedInputTokens ?? 0;
333
+ // Accumulate tokens across all steps
334
+ totalInputTokens += stepInputTokens;
335
+ totalOutputTokens += stepOutputTokens;
336
+ totalCachedTokens += stepCachedTokens;
337
+ }
338
+ const toolsCalled = step.content
339
+ .filter((c) => c.type === "tool-result")
340
+ .map((c) => ({
341
+ toolName: c.toolName,
342
+ input: JSON.stringify(c.input),
343
+ output: JSON.stringify(c.output, null, 2),
344
+ }));
345
+ if (toolsCalled.length > 0) {
346
+ logRef.content += `[TOOLS CALLED]\n`;
347
+ toolsCalled.forEach((tool, idx) => {
348
+ logRef.content += ` Tool ${idx + 1}: ${tool.toolName}\n`;
349
+ logRef.content += ` Input: ${tool.input}\n`;
350
+ logRef.content += ` Output: ${tool.output}\n`;
351
+ });
352
+ toolsCalled.forEach((tool, idx) => {
353
+ let parsedInput, parsedOutput;
354
+ try {
355
+ parsedInput = JSON.parse(tool.input);
356
+ }
357
+ catch {
358
+ parsedInput = tool.input;
359
+ }
360
+ try {
361
+ parsedOutput = JSON.parse(tool.output);
362
+ }
363
+ catch {
364
+ parsedOutput = tool.output;
365
+ }
366
+ services.clarkProfiler
367
+ .getProfiler()
368
+ .addInstantEvent(`API Tool Call: ${tool.toolName}`, apiSubagentTrack, {
369
+ step: stepCount,
370
+ toolIndex: idx + 1,
371
+ toolName: tool.toolName,
372
+ input: parsedInput,
373
+ output: parsedOutput,
374
+ inputSize: tool.input.length,
375
+ outputSize: tool.output.length,
376
+ apiName,
377
+ });
521
378
  });
522
- });
379
+ }
380
+ logRef.content += `\n`;
381
+ },
382
+ onFinish: (result) => {
383
+ context.endTurn(result.totalUsage);
384
+ if (mainSystemPrompt) {
385
+ // restore main system prompt
386
+ context.setSystemPrompt(mainSystemPrompt);
387
+ }
388
+ },
389
+ }, clark.tracer, clark.logger);
390
+ for await (const chunk of response.fullStream) {
391
+ await processStreamChunk(chunk, clark, logRef, `api-${apiName}`);
392
+ }
393
+ if (thinkingSpanActive) {
394
+ services.clarkProfiler.getProfiler().endFrame(apiSubagentTrack);
395
+ }
396
+ if (textSpanActive) {
397
+ services.clarkProfiler.getProfiler().endFrame(apiSubagentTrack);
398
+ }
399
+ try {
400
+ const requestTokenData = {
401
+ requestId: `api-subagent-${apiName}-${Date.now()}`,
402
+ inputTokens: totalInputTokens,
403
+ outputTokens: totalOutputTokens,
404
+ totalTokens: totalInputTokens + totalOutputTokens,
405
+ cachedInputTokens: totalCachedTokens,
406
+ model: model.modelId,
407
+ startTime: startTime,
408
+ endTime: new Date().toISOString(),
409
+ };
410
+ await clark.context.peer?.call.aiPushTokenUsage(requestTokenData);
411
+ }
412
+ catch (error) {
413
+ // Token tracking is non-critical - log error but don't fail the API generation
414
+ getLogger().warn("Failed to send token usage data for API generation", error instanceof Error ? error.message : String(error));
415
+ }
416
+ const stateMap = clark.context.apiGenStateMap;
417
+ if (!stateMap) {
418
+ throw new Error("No API generation state map found in context");
419
+ }
420
+ const finalState = stateMap.get(id);
421
+ if (!finalState) {
422
+ throw new Error("No state found for API ${pageName}/${apiName} after generation");
423
+ }
424
+ try {
425
+ if (finalState.status === "finalized") {
426
+ logger.info(`API generation succeeded for ${pageName}/${apiName}`);
427
+ return finalState;
523
428
  }
524
- logRef.content += `\n`;
525
- },
526
- onFinish: (result) => {
527
- context.endTurn(result.totalUsage);
528
- if (mainSystemPrompt) {
529
- // restore main system prompt
530
- context.setSystemPrompt(mainSystemPrompt);
429
+ else {
430
+ logger.error(`API generation failed for ${pageName}/${apiName}: final status: ${finalState.status}`);
431
+ throw new Error(`API generation failed, final state: ${safeJsonStringify(finalState)}`);
531
432
  }
532
- },
533
- }, clark.tracer, clark.logger);
534
- for await (const chunk of response.fullStream) {
535
- await processStreamChunk(chunk, clark, logRef, `api-${apiName}`);
536
- }
537
- if (thinkingSpanActive) {
538
- services.clarkProfiler.getProfiler().endFrame(apiSubagentTrack);
539
- }
540
- if (textSpanActive) {
541
- services.clarkProfiler.getProfiler().endFrame(apiSubagentTrack);
542
- }
543
- try {
544
- const requestTokenData = {
545
- requestId: `api-subagent-${apiName}-${Date.now()}`,
546
- inputTokens: totalInputTokens,
547
- outputTokens: totalOutputTokens,
548
- totalTokens: totalInputTokens + totalOutputTokens,
549
- cachedInputTokens: totalCachedTokens,
550
- model: model.modelId,
551
- startTime: startTime,
552
- endTime: new Date().toISOString(),
553
- };
554
- await clark.context.peer?.call.aiPushTokenUsage(requestTokenData);
555
- }
556
- catch (error) {
557
- // Token tracking is non-critical - log error but don't fail the API generation
558
- getLogger().warn("Failed to send token usage data for API generation", error instanceof Error ? error.message : String(error));
559
- }
560
- return {
561
- message: finalizedMessage,
433
+ }
434
+ finally {
435
+ stateMap.delete(id);
436
+ }
437
+ }, apiSubagentTrack, {
562
438
  apiName,
563
439
  pageName,
564
- apiData: finalizeApiData,
565
- };
566
- }, apiSubagentTrack, {
567
- apiName,
568
- pageName,
569
- promptLength: apiPrompt.length,
570
- model: model.modelId,
571
- });
440
+ promptLength: apiPrompt.length,
441
+ model: model.modelId,
442
+ });
443
+ }
444
+ finally {
445
+ // Always release the context lock when done
446
+ contextHandle.release();
447
+ }
572
448
  },
573
449
  };
574
450
  });