From 87cbbc140358336c1b06ffe219a29c7d0d7d1c26 Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 10:42:43 -0600 Subject: [PATCH 01/11] fix: remove localhost agents-api bypass, use CMA for all agent calls [INTEG-3825] --- apps/google-docs/src/services/agents-api.ts | 83 ++----------------- apps/google-docs/src/utils/constants/agent.ts | 2 - 2 files changed, 8 insertions(+), 77 deletions(-) diff --git a/apps/google-docs/src/services/agents-api.ts b/apps/google-docs/src/services/agents-api.ts index 7c855e7e6c..d3904138ec 100644 --- a/apps/google-docs/src/services/agents-api.ts +++ b/apps/google-docs/src/services/agents-api.ts @@ -1,5 +1,5 @@ import { PageAppSDK } from '@contentful/app-sdk'; -import { LOCAL_AGENTS_API_BASE_URL, WORKFLOW_AGENT_ID } from '../utils/constants/agent'; +import { WORKFLOW_AGENT_ID } from '../utils/constants/agent'; import { AgentRunMessage, MappingReviewSuspendPayload, @@ -8,11 +8,6 @@ import { TabsImagesSuspendPayload, } from '@types'; -const AGENTS_API_HEADERS = { - 'x-contentful-enable-alpha-feature': 'agents-api', - 'X-Contentful-App-Definition-Id': '653vTnuQw3j5onU1tUoH6t', -}; - export interface AgentGeneratePayload { messages: Array<{ role: 'user'; @@ -46,38 +41,12 @@ export interface AgentRunData { error?: Record; } -function getJsonHeaders(): HeadersInit { - return { - ...AGENTS_API_HEADERS, - 'Content-Type': 'application/json', - }; -} - export async function getWorkflowRun( sdk: PageAppSDK, spaceId: string, environmentId: string, runId: string ): Promise { - if (LOCAL_AGENTS_API_BASE_URL) { - const response = await fetch( - `${LOCAL_AGENTS_API_BASE_URL}/spaces/${spaceId}/environments/${environmentId}/ai_agents/runs/${runId}`, - { - headers: AGENTS_API_HEADERS, - } - ); - - if (response.status === 404) { - return null; - } - - if (!response.ok) { - throw new Error(`Failed to poll agent run: ${response.status} ${response.statusText}`); - } - - return (await response.json()) as AgentRunData; - } - try { return (await sdk.cma.agentRun.get({ spaceId, @@ -102,32 +71,13 @@ export async function startAgentRun( ): Promise { let runData: AgentRunData; - if (LOCAL_AGENTS_API_BASE_URL) { - const response = await fetch( - `${LOCAL_AGENTS_API_BASE_URL}/spaces/${spaceId}/environments/${environmentId}/ai_agents/agents/${WORKFLOW_AGENT_ID}/generate`, - { - method: 'POST', - headers: getJsonHeaders(), - body: JSON.stringify(payload), - } - ); - - if (!response.ok) { - throw new Error( - `Failed to start workflow agent run: ${response.status} ${response.statusText}` - ); - } - - runData = (await response.json()) as AgentRunData; - } else { - try { - runData = (await sdk.cma.agent.generate( - { agentId: WORKFLOW_AGENT_ID, spaceId, environmentId }, - payload - )) as AgentRunData; - } catch (error) { - throw new Error(`Failed to start workflow agent run: ${error as Error}`); - } + try { + runData = (await sdk.cma.agent.generate( + { agentId: WORKFLOW_AGENT_ID, spaceId, environmentId }, + payload + )) as AgentRunData; + } catch (error) { + throw new Error(`Failed to start workflow agent run: ${error as Error}`); } if (!runData.sys?.id) { @@ -150,23 +100,6 @@ export async function resumeWorkflowRun( runId: string, resumePayload: ResumePayload ): Promise { - if (LOCAL_AGENTS_API_BASE_URL) { - const response = await fetch( - `${LOCAL_AGENTS_API_BASE_URL}/spaces/${spaceId}/environments/${environmentId}/ai_agents/runs/${runId}/resume`, - { - method: 'POST', - headers: getJsonHeaders(), - body: JSON.stringify({ resumePayload }), - } - ); - - if (!response.ok) { - throw new Error(`Failed to resume agent run: ${response.status} ${response.statusText}`); - } - - return; - } - const agentRunApi = sdk.cma.agentRun as { resume?: ( params: { spaceId: string; environmentId: string; runId: string }, diff --git a/apps/google-docs/src/utils/constants/agent.ts b/apps/google-docs/src/utils/constants/agent.ts index 2a1b13bfd4..65632ef987 100644 --- a/apps/google-docs/src/utils/constants/agent.ts +++ b/apps/google-docs/src/utils/constants/agent.ts @@ -6,6 +6,4 @@ const MAX_POLL_TIME_MS = 5 * 60 * 1000 * 10; // 50 minutes export const MAX_POLL_ATTEMPTS = Math.floor(MAX_POLL_TIME_MS / POLL_INTERVAL_MS); -export const LOCAL_AGENTS_API_BASE_URL = 'http://localhost:4111'; - export const CONTENT_TYPE_SUBMIT_LOADING_DELAY_MS = 15000; // 15 seconds to wait for suspend payload From 2e2688044165c8aa97b0d064ad58beeb7b6035ae Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 10:53:09 -0600 Subject: [PATCH 02/11] chore(google-docs): bump app-sdk to 4.54.0 and react-apps-toolkit to 1.2.22 [INTEG-3825] --- apps/google-docs/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/google-docs/package.json b/apps/google-docs/package.json index fe7fca25c1..072bd0206a 100644 --- a/apps/google-docs/package.json +++ b/apps/google-docs/package.json @@ -4,14 +4,14 @@ "private": true, "dependencies": { "@ai-sdk/openai": "^2.0.56", - "@contentful/app-sdk": "^4.51.3", + "@contentful/app-sdk": "^4.54.0", "@contentful/f36-components": "^5.6.0", "@contentful/f36-icons": "^6.7.1", "@contentful/f36-multiselect": "^6.7.1", "@contentful/f36-tokens": "^5.1.0", "@contentful/field-editor-json": "^3.3.38", "@contentful/node-apps-toolkit": "^3.13.0", - "@contentful/react-apps-toolkit": "^1.2.16", + "@contentful/react-apps-toolkit": "^1.2.22", "@emotion/css": "^11.13.4", "ai": "^5.0.81", "contentful-management": "^11.61.0", From 8fbf903de248a922115d2e146b882e735753193e Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 10:54:40 -0600 Subject: [PATCH 03/11] chore(google-docs): update package-lock.json after sdk version bumps [INTEG-3825] --- apps/google-docs/package-lock.json | 95 +++++++++++++++++------------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/apps/google-docs/package-lock.json b/apps/google-docs/package-lock.json index ee813f1a5f..f2cebf666e 100644 --- a/apps/google-docs/package-lock.json +++ b/apps/google-docs/package-lock.json @@ -9,14 +9,14 @@ "version": "0.1.0", "dependencies": { "@ai-sdk/openai": "^2.0.56", - "@contentful/app-sdk": "^4.51.3", + "@contentful/app-sdk": "^4.54.0", "@contentful/f36-components": "^5.6.0", "@contentful/f36-icons": "^6.7.1", "@contentful/f36-multiselect": "^6.7.1", "@contentful/f36-tokens": "^5.1.0", "@contentful/field-editor-json": "^3.3.38", "@contentful/node-apps-toolkit": "^3.13.0", - "@contentful/react-apps-toolkit": "^1.2.16", + "@contentful/react-apps-toolkit": "^1.2.22", "@emotion/css": "^11.13.4", "ai": "^5.0.81", "contentful-management": "^11.61.0", @@ -533,12 +533,27 @@ } }, "node_modules/@contentful/app-sdk": { - "version": "4.51.3", - "resolved": "https://registry.npmjs.org/@contentful/app-sdk/-/app-sdk-4.51.3.tgz", - "integrity": "sha512-oYY593sFXsR47gP9Q+p6E+9EaH6SSa9dIs1kiekKT1NDdMMiAlgj7aXlaH/kau0GkR6XVxURSb9mD4I6gb6pCw==", - "license": "MIT", + "version": "4.54.0", + "resolved": "https://registry.npmjs.org/@contentful/app-sdk/-/app-sdk-4.54.0.tgz", + "integrity": "sha512-jKeBeX3soas72resUNRnWP3rTurFCzZ/47I0v+6/a9JoKojYTd8eXMGMRZ8KmGuo8GIle5PNB7ublMVMJumscg==", "dependencies": { - "contentful-management": "^11.67.0" + "contentful-management": "^12.3.1" + } + }, + "node_modules/@contentful/app-sdk/node_modules/contentful-management": { + "version": "12.3.1", + "resolved": "https://registry.npmjs.org/contentful-management/-/contentful-management-12.3.1.tgz", + "integrity": "sha512-Lnx6ieod2z88AhZwcV2kDhN5stctq+0ifflX+xepphPJ1a2RsPNonCAcySabPETuoNox8G0kXo5ebNybrPjOYw==", + "dependencies": { + "@contentful/rich-text-types": "^16.6.1", + "axios": "^1.15.0", + "contentful-sdk-core": "^9.4.4", + "fast-copy": "^3.0.0", + "globals": "^15.15.0", + "process": "^0.11.10" + }, + "engines": { + "node": ">=20" } }, "node_modules/@contentful/f36-accordion": { @@ -2177,9 +2192,9 @@ } }, "node_modules/@contentful/react-apps-toolkit": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@contentful/react-apps-toolkit/-/react-apps-toolkit-1.2.16.tgz", - "integrity": "sha512-vx7+T4h+w1XBbn7OfRoXr8YUTtiyRNLaltF2ziM4xMqBryu/3Y71nBbhbxD6NIfOui3QIo3TR9Q5sPTZvGbIVw==", + "version": "1.2.22", + "resolved": "https://registry.npmjs.org/@contentful/react-apps-toolkit/-/react-apps-toolkit-1.2.22.tgz", + "integrity": "sha512-xlVHpHfUTQlwWqCfprJh9Q7nt/hRBNnhdaobyGBlVeiN7qPG4O0vsbIoDt86rB1uryki91g0uLtKLtQNdQWjeA==", "dependencies": { "contentful-management": ">=7.30.0" }, @@ -4639,13 +4654,13 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.1.tgz", - "integrity": "sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.1.tgz", + "integrity": "sha512-WOG+Jj8ZOvR0a3rAn+Tuf1UQJRxw5venr6DgdbJzngJE3qG7X0kL83CZGpdHMxEm+ZK3seAbvFsw4FfOfP9vxg==", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" } }, "node_modules/babel-plugin-emotion": { @@ -5142,15 +5157,14 @@ } }, "node_modules/contentful-sdk-core": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-9.2.1.tgz", - "integrity": "sha512-Y+Qz2tGYE2ia6o42R8DG4uFSMSLJ6qfAbKQLU6p2+KxsqvbK1Fg60wKVKF+FHMShuCnlg2EwToOvxVsM2N+BrQ==", + "version": "9.4.5", + "resolved": "https://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-9.4.5.tgz", + "integrity": "sha512-8eGTMO11LXFAiosaiV38bjvW6cjoQFtNE38pNtVilZTXpmGFinClljihH0eriv11Ispd4q82TqtsXMJPYD/C+A==", "dependencies": { "fast-copy": "^3.0.2", - "lodash": "^4.17.21", - "p-throttle": "^6.1.0", + "lodash": "^4.17.23", "process": "^0.11.10", - "qs": "^6.12.3" + "qs": "^6.15.0" }, "engines": { "node": ">=18" @@ -5159,6 +5173,11 @@ "@rollup/rollup-linux-x64-gnu": "^4.18.0" } }, + "node_modules/contentful-sdk-core/node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -5839,9 +5858,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -7380,17 +7399,6 @@ "node": ">=8" } }, - "node_modules/p-throttle": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-throttle/-/p-throttle-6.2.0.tgz", - "integrity": "sha512-NCKkOVj6PZa6NiTmfvGilDdf6vO1rFCD3KDnkHko8dTOtkpk4cSR/VTAhhLMG9aiQ7/A9HYgEDNmxzf6hxzR3g==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", @@ -7606,9 +7614,12 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "engines": { + "node": ">=10" + } }, "node_modules/pump": { "version": "3.0.3", @@ -7630,9 +7641,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", "dependencies": { "side-channel": "^1.1.0" }, From 342f7255679c1318405bcd04bc5e579275db035b Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 10:57:17 -0600 Subject: [PATCH 04/11] fix(google-docs): use sdk.cma.agentRun.resumeRun instead of manual cast [INTEG-3825] --- apps/google-docs/src/services/agents-api.ts | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/apps/google-docs/src/services/agents-api.ts b/apps/google-docs/src/services/agents-api.ts index d3904138ec..695a640d37 100644 --- a/apps/google-docs/src/services/agents-api.ts +++ b/apps/google-docs/src/services/agents-api.ts @@ -100,16 +100,5 @@ export async function resumeWorkflowRun( runId: string, resumePayload: ResumePayload ): Promise { - const agentRunApi = sdk.cma.agentRun as { - resume?: ( - params: { spaceId: string; environmentId: string; runId: string }, - body: { resumePayload: ResumePayload } - ) => Promise; - }; - - if (!agentRunApi.resume) { - throw new Error('Agent run resume is not available in the current SDK.'); - } - - await agentRunApi.resume({ spaceId, environmentId, runId }, { resumePayload }); + await sdk.cma.agentRun.resumeRun({ spaceId, environmentId, runId }, { resumePayload }); } From a8bc58a8beb6fb1571d352edba9cc7b292c1eb4f Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 11:05:47 -0600 Subject: [PATCH 05/11] chore(google-docs): bump contentful-management to 11.76.0 [INTEG-3825] --- apps/google-docs/package-lock.json | 12 ++++++------ apps/google-docs/package.json | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/google-docs/package-lock.json b/apps/google-docs/package-lock.json index f2cebf666e..017e099e7c 100644 --- a/apps/google-docs/package-lock.json +++ b/apps/google-docs/package-lock.json @@ -19,7 +19,7 @@ "@contentful/react-apps-toolkit": "^1.2.22", "@emotion/css": "^11.13.4", "ai": "^5.0.81", - "contentful-management": "^11.61.0", + "contentful-management": "^11.76.0", "googleapis": "^166.0.0", "mammoth": "^1.11.0", "react": "^18.3.1", @@ -5142,13 +5142,13 @@ "dev": true }, "node_modules/contentful-management": { - "version": "11.67.0", - "resolved": "https://registry.npmjs.org/contentful-management/-/contentful-management-11.67.0.tgz", - "integrity": "sha512-Al6Dm9eBoLfBERl1nkYWLHGMSR98CCTPdsh466k2tjjlSGDAT3B2D/BnNTzpzidW5vYGIoVjRezGvcedV3vxfg==", + "version": "11.76.0", + "resolved": "https://registry.npmjs.org/contentful-management/-/contentful-management-11.76.0.tgz", + "integrity": "sha512-KsqIZ65q1A6IP55sxTfuAMhBTNJfgGxlVZBoV2ghBuR1LaZZyTqway5vj9KHRDl/Z1uOQQsYSiz+FayMxBXXEw==", "dependencies": { "@contentful/rich-text-types": "^16.6.1", - "axios": "^1.12.2", - "contentful-sdk-core": "^9.0.1", + "axios": "^1.15.0", + "contentful-sdk-core": "^9.4.4", "fast-copy": "^3.0.0", "globals": "^15.15.0" }, diff --git a/apps/google-docs/package.json b/apps/google-docs/package.json index 072bd0206a..c6ba1643e2 100644 --- a/apps/google-docs/package.json +++ b/apps/google-docs/package.json @@ -14,7 +14,7 @@ "@contentful/react-apps-toolkit": "^1.2.22", "@emotion/css": "^11.13.4", "ai": "^5.0.81", - "contentful-management": "^11.61.0", + "contentful-management": "^11.76.0", "googleapis": "^166.0.0", "mammoth": "^1.11.0", "react": "^18.3.1", From 779159fc2d0035bb9c0530550d47ed18377f8a6b Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 11:13:56 -0600 Subject: [PATCH 06/11] fix(google-docs): replace localhost URL flag with USE_LOCAL_AGENTS_API boolean [INTEG-3825] --- apps/google-docs/src/services/agents-api.ts | 83 +++++++++++++++++-- apps/google-docs/src/utils/constants/agent.ts | 4 + 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/apps/google-docs/src/services/agents-api.ts b/apps/google-docs/src/services/agents-api.ts index 695a640d37..91b9ccd1c2 100644 --- a/apps/google-docs/src/services/agents-api.ts +++ b/apps/google-docs/src/services/agents-api.ts @@ -1,5 +1,5 @@ import { PageAppSDK } from '@contentful/app-sdk'; -import { WORKFLOW_AGENT_ID } from '../utils/constants/agent'; +import { LOCAL_AGENTS_API_BASE_URL, USE_LOCAL_AGENTS_API, WORKFLOW_AGENT_ID } from '../utils/constants/agent'; import { AgentRunMessage, MappingReviewSuspendPayload, @@ -8,6 +8,18 @@ import { TabsImagesSuspendPayload, } from '@types'; +const AGENTS_API_HEADERS = { + 'x-contentful-enable-alpha-feature': 'agents-api', + 'X-Contentful-App-Definition-Id': '653vTnuQw3j5onU1tUoH6t', +}; + +function getJsonHeaders(): HeadersInit { + return { + ...AGENTS_API_HEADERS, + 'Content-Type': 'application/json', + }; +} + export interface AgentGeneratePayload { messages: Array<{ role: 'user'; @@ -47,6 +59,25 @@ export async function getWorkflowRun( environmentId: string, runId: string ): Promise { + if (USE_LOCAL_AGENTS_API) { + const response = await fetch( + `${LOCAL_AGENTS_API_BASE_URL}/spaces/${spaceId}/environments/${environmentId}/ai_agents/runs/${runId}`, + { + headers: AGENTS_API_HEADERS, + } + ); + + if (response.status === 404) { + return null; + } + + if (!response.ok) { + throw new Error(`Failed to poll agent run: ${response.status} ${response.statusText}`); + } + + return (await response.json()) as AgentRunData; + } + try { return (await sdk.cma.agentRun.get({ spaceId, @@ -71,13 +102,32 @@ export async function startAgentRun( ): Promise { let runData: AgentRunData; - try { - runData = (await sdk.cma.agent.generate( - { agentId: WORKFLOW_AGENT_ID, spaceId, environmentId }, - payload - )) as AgentRunData; - } catch (error) { - throw new Error(`Failed to start workflow agent run: ${error as Error}`); + if (USE_LOCAL_AGENTS_API) { + const response = await fetch( + `${LOCAL_AGENTS_API_BASE_URL}/spaces/${spaceId}/environments/${environmentId}/ai_agents/agents/${WORKFLOW_AGENT_ID}/generate`, + { + method: 'POST', + headers: getJsonHeaders(), + body: JSON.stringify(payload), + } + ); + + if (!response.ok) { + throw new Error( + `Failed to start workflow agent run: ${response.status} ${response.statusText}` + ); + } + + runData = (await response.json()) as AgentRunData; + } else { + try { + runData = (await sdk.cma.agent.generate( + { agentId: WORKFLOW_AGENT_ID, spaceId, environmentId }, + payload + )) as AgentRunData; + } catch (error) { + throw new Error(`Failed to start workflow agent run: ${error as Error}`); + } } if (!runData.sys?.id) { @@ -100,5 +150,22 @@ export async function resumeWorkflowRun( runId: string, resumePayload: ResumePayload ): Promise { + if (USE_LOCAL_AGENTS_API) { + const response = await fetch( + `${LOCAL_AGENTS_API_BASE_URL}/spaces/${spaceId}/environments/${environmentId}/ai_agents/runs/${runId}/resume`, + { + method: 'POST', + headers: getJsonHeaders(), + body: JSON.stringify({ resumePayload }), + } + ); + + if (!response.ok) { + throw new Error(`Failed to resume agent run: ${response.status} ${response.statusText}`); + } + + return; + } + await sdk.cma.agentRun.resumeRun({ spaceId, environmentId, runId }, { resumePayload }); } diff --git a/apps/google-docs/src/utils/constants/agent.ts b/apps/google-docs/src/utils/constants/agent.ts index 65632ef987..c432097520 100644 --- a/apps/google-docs/src/utils/constants/agent.ts +++ b/apps/google-docs/src/utils/constants/agent.ts @@ -7,3 +7,7 @@ const MAX_POLL_TIME_MS = 5 * 60 * 1000 * 10; // 50 minutes export const MAX_POLL_ATTEMPTS = Math.floor(MAX_POLL_TIME_MS / POLL_INTERVAL_MS); export const CONTENT_TYPE_SUBMIT_LOADING_DELAY_MS = 15000; // 15 seconds to wait for suspend payload + +export const LOCAL_AGENTS_API_BASE_URL = 'http://localhost:4111'; +// Set to true to route agent calls to the local dev server instead of the CMA +export const USE_LOCAL_AGENTS_API = false; From 70dbecb5e1775320d30bd1f8dd1d95ff93c39d29 Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 13:01:44 -0600 Subject: [PATCH 07/11] chore(google-docs): bump app-sdk to 4.55.1 [INTEG-3825] --- apps/google-docs/package-lock.json | 8 ++++---- apps/google-docs/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/google-docs/package-lock.json b/apps/google-docs/package-lock.json index 017e099e7c..0ea77b9acf 100644 --- a/apps/google-docs/package-lock.json +++ b/apps/google-docs/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "@ai-sdk/openai": "^2.0.56", - "@contentful/app-sdk": "^4.54.0", + "@contentful/app-sdk": "^4.55.1", "@contentful/f36-components": "^5.6.0", "@contentful/f36-icons": "^6.7.1", "@contentful/f36-multiselect": "^6.7.1", @@ -533,9 +533,9 @@ } }, "node_modules/@contentful/app-sdk": { - "version": "4.54.0", - "resolved": "https://registry.npmjs.org/@contentful/app-sdk/-/app-sdk-4.54.0.tgz", - "integrity": "sha512-jKeBeX3soas72resUNRnWP3rTurFCzZ/47I0v+6/a9JoKojYTd8eXMGMRZ8KmGuo8GIle5PNB7ublMVMJumscg==", + "version": "4.55.1", + "resolved": "https://registry.npmjs.org/@contentful/app-sdk/-/app-sdk-4.55.1.tgz", + "integrity": "sha512-u/Z1ishBOGyXSq7E4T2wvEeS5FC83E5o/qYJXHWef99Dr/fYddFhILfj24ryfSCw0GNVnkL0yetxy5SxmB9gNQ==", "dependencies": { "contentful-management": "^12.3.1" } diff --git a/apps/google-docs/package.json b/apps/google-docs/package.json index c6ba1643e2..64c745ec4f 100644 --- a/apps/google-docs/package.json +++ b/apps/google-docs/package.json @@ -4,7 +4,7 @@ "private": true, "dependencies": { "@ai-sdk/openai": "^2.0.56", - "@contentful/app-sdk": "^4.54.0", + "@contentful/app-sdk": "^4.55.1", "@contentful/f36-components": "^5.6.0", "@contentful/f36-icons": "^6.7.1", "@contentful/f36-multiselect": "^6.7.1", From 936fa3a57663b233118a1e2b7ef64e03ec74d7c6 Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 13:10:41 -0600 Subject: [PATCH 08/11] fix(google-docs): cast resumePayload to satisfy AgentResumeRunPayload type [INTEG-3825] --- apps/google-docs/src/services/agents-api.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/google-docs/src/services/agents-api.ts b/apps/google-docs/src/services/agents-api.ts index 91b9ccd1c2..423bd55307 100644 --- a/apps/google-docs/src/services/agents-api.ts +++ b/apps/google-docs/src/services/agents-api.ts @@ -167,5 +167,8 @@ export async function resumeWorkflowRun( return; } - await sdk.cma.agentRun.resumeRun({ spaceId, environmentId, runId }, { resumePayload }); + await sdk.cma.agentRun.resumeRun( + { spaceId, environmentId, runId }, + { resumePayload: resumePayload as Record } + ); } From 2944acad909cbb28169f3c8e976a6bca49fad124 Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 14:14:19 -0600 Subject: [PATCH 09/11] fix(google-docs): correct suspendStepId for tabs/images step to document-scope-selection [INTEG-3825] --- apps/google-docs/src/types/workflow.ts | 2 +- .../Page/components/mainpage/ModalOrchestrator.spec.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/google-docs/src/types/workflow.ts b/apps/google-docs/src/types/workflow.ts index a5730be0a2..8217a827c4 100644 --- a/apps/google-docs/src/types/workflow.ts +++ b/apps/google-docs/src/types/workflow.ts @@ -77,7 +77,7 @@ export interface CompletedWorkflowPayload { export interface TabsImagesSuspendPayload { reason?: string; - suspendStepId: 'select-tabs-images-step'; + suspendStepId: 'document-scope-selection'; documentId?: string; title?: string; requiresImageSelection?: boolean; diff --git a/apps/google-docs/test/locations/Page/components/mainpage/ModalOrchestrator.spec.tsx b/apps/google-docs/test/locations/Page/components/mainpage/ModalOrchestrator.spec.tsx index c588011209..4c342f748f 100644 --- a/apps/google-docs/test/locations/Page/components/mainpage/ModalOrchestrator.spec.tsx +++ b/apps/google-docs/test/locations/Page/components/mainpage/ModalOrchestrator.spec.tsx @@ -102,7 +102,7 @@ describe('ModalOrchestrator', () => { runId: 'run-123', messages: [], suspendPayload: { - suspendStepId: 'select-tabs-images-step', + suspendStepId: 'document-scope-selection', reason: 'Needs document scope review', documentId: 'mock-doc-id-123', requiresImageSelection: true, @@ -315,7 +315,7 @@ describe('ModalOrchestrator', () => { runId: 'run-123', messages: [], suspendPayload: { - suspendStepId: 'select-tabs-images-step', + suspendStepId: 'document-scope-selection', reason: 'Needs document scope review', documentId: 'mock-doc-id-123', requiresImageSelection: true, From 322bfcd11b615be0fe32051fd6419a41e64b3804 Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Tue, 21 Apr 2026 14:16:21 -0600 Subject: [PATCH 10/11] chore(google-docs): add debug logging to include-images resume flow [INTEG-3825] --- .../components/mainpage/ModalOrchestrator.tsx | 5 ++++- apps/google-docs/src/services/agents-api.ts | 15 +++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/google-docs/src/locations/Page/components/mainpage/ModalOrchestrator.tsx b/apps/google-docs/src/locations/Page/components/mainpage/ModalOrchestrator.tsx index 4d9c1821e7..26e3b4b61e 100644 --- a/apps/google-docs/src/locations/Page/components/mainpage/ModalOrchestrator.tsx +++ b/apps/google-docs/src/locations/Page/components/mainpage/ModalOrchestrator.tsx @@ -181,9 +181,11 @@ export const ModalOrchestrator = forwardRef } - ); + try { + console.log('[resumeWorkflowRun] calling sdk.cma.agentRun.resumeRun for run', runId, 'payload:', resumePayload); + await sdk.cma.agentRun.resumeRun( + { spaceId, environmentId, runId }, + { resumePayload: resumePayload as Record } + ); + console.log('[resumeWorkflowRun] resumeRun succeeded'); + } catch (error) { + console.error('[resumeWorkflowRun] resumeRun failed:', error); + throw error; + } } From f3bccc1991f25a8e88fc3102ce2d2a520e0b9e6d Mon Sep 17 00:00:00 2001 From: ryunsong-contentful Date: Wed, 22 Apr 2026 13:47:25 -0600 Subject: [PATCH 11/11] chore(google-docs): add debug logging to startAgentRun, getWorkflowRun, and FAILED run handler [INTEG-3825] --- apps/google-docs/src/hooks/useWorkflowAgent.ts | 1 + apps/google-docs/src/services/agents-api.ts | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/google-docs/src/hooks/useWorkflowAgent.ts b/apps/google-docs/src/hooks/useWorkflowAgent.ts index d4a5b7807e..c008e5887c 100644 --- a/apps/google-docs/src/hooks/useWorkflowAgent.ts +++ b/apps/google-docs/src/hooks/useWorkflowAgent.ts @@ -118,6 +118,7 @@ const getWorkflowRunResult = ( switch (status) { case RunStatus.FAILED: + console.error('[getWorkflowRunResult] run FAILED. Full runData:', JSON.stringify(runData)); throw new Error(getRunErrorMessage(runData)); case RunStatus.PENDING_REVIEW: { diff --git a/apps/google-docs/src/services/agents-api.ts b/apps/google-docs/src/services/agents-api.ts index 42b192a21b..12eddf38dd 100644 --- a/apps/google-docs/src/services/agents-api.ts +++ b/apps/google-docs/src/services/agents-api.ts @@ -79,12 +79,16 @@ export async function getWorkflowRun( } try { - return (await sdk.cma.agentRun.get({ + console.log('[getWorkflowRun] calling sdk.cma.agentRun.get for run', runId); + const result = (await sdk.cma.agentRun.get({ spaceId, environmentId, runId, })) as AgentRunData; + console.log('[getWorkflowRun] run status:', result?.sys?.status ?? result?.metadata?.status, 'full:', JSON.stringify(result)); + return result; } catch (error: unknown) { + console.error('[getWorkflowRun] error:', error); const err = error as { code?: string }; if (err?.code === 'NotFound') { return null; @@ -121,19 +125,24 @@ export async function startAgentRun( runData = (await response.json()) as AgentRunData; } else { try { + console.log('[startAgentRun] calling sdk.cma.agent.generate for agent', WORKFLOW_AGENT_ID, 'space', spaceId, 'env', environmentId, 'threadId', payload.threadId); runData = (await sdk.cma.agent.generate( { agentId: WORKFLOW_AGENT_ID, spaceId, environmentId }, payload )) as AgentRunData; + console.log('[startAgentRun] generate response:', JSON.stringify(runData)); } catch (error) { + console.error('[startAgentRun] sdk.cma.agent.generate failed:', error); throw new Error(`Failed to start workflow agent run: ${error as Error}`); } } if (!runData.sys?.id) { + console.error('[startAgentRun] no run ID in response:', JSON.stringify(runData)); throw new Error('Agent run started but no run ID was returned'); } + console.log('[startAgentRun] run started with ID', runData.sys.id); return runData.sys.id; }