thinkwork-cli 0.12.16 → 0.12.17

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 (149) hide show
  1. package/dist/cli.js +517 -10403
  2. package/dist/commands/enterprise/templates/deploy-repo/docs/runbook.md +3 -8
  3. package/dist/commands/enterprise/templates/deploy-repo/terraform/main.tf +1 -184
  4. package/dist/drizzle/0050_brain_v0_entity_subtype.sql +2 -2
  5. package/dist/drizzle/0051_brain_v0_tenant_entity_pages.sql +6 -6
  6. package/dist/drizzle/0052_brain_v0_external_refs.sql +2 -2
  7. package/dist/drizzle/0053_brain_v0_unresolved_mentions_subtype.sql +2 -2
  8. package/dist/drizzle/0145_knowledge_graph_thread_ingest.sql +14 -14
  9. package/dist/drizzle/0146_knowledge_graph_source_scope.sql +1 -1
  10. package/dist/drizzle/0157_knowledge_graph_observations_source.sql +2 -2
  11. package/dist/drizzle/0158_wiki_tenant_scope.sql +1 -1
  12. package/dist/drizzle/0165_plugin_premium_entitlements.sql +2 -2
  13. package/dist/drizzle/{0166_company_brain_substrate_contract.sql → 0166_brain_substrate_contract.sql} +9 -9
  14. package/dist/drizzle/{0167_company_brain_artifact_manifest_runtime.sql → 0167_brain_artifact_manifest_runtime.sql} +5 -5
  15. package/dist/drizzle/0168_eval_skill_gate.sql +1 -1
  16. package/dist/drizzle/0183_okf_artifact_manifests.sql +1 -1
  17. package/dist/drizzle/0200_eval_results_execution_tier.sql +40 -0
  18. package/dist/drizzle/0201_brain_dream_runs.sql +72 -0
  19. package/dist/drizzle/0202_backfill_thread_participants_from_senders.sql +110 -0
  20. package/dist/drizzle/0203_threads_mode_override.sql +61 -0
  21. package/dist/drizzle/0204_thread_turns_triggering_message_id.sql +48 -0
  22. package/dist/drizzle/0205_routines_git_python.sql +185 -0
  23. package/dist/drizzle/0206_agent_loop_versions_routine_actions.sql +40 -0
  24. package/dist/drizzle/0207_tenant_credentials_github_repo_kind.sql +52 -0
  25. package/dist/drizzle/0208_knowledge_graph_neutral_identifiers.sql +253 -0
  26. package/dist/drizzle/0209_living_artifacts_schema.sql +109 -0
  27. package/dist/drizzle/0210_think137_automations_additive_schema.sql +113 -0
  28. package/dist/drizzle/0211_backfill_agent_loop_versions_target_spec.sql +120 -0
  29. package/dist/drizzle/0212_fold_webhooks_into_automations.sql +312 -0
  30. package/dist/drizzle/0213_agent_loop_versions_judge_evidence_nullable.sql +63 -0
  31. package/dist/drizzle/0214_think137_drop_judge_evidence_roi.sql +99 -0
  32. package/dist/drizzle/0215_agent_loop_versions_goal_worker_policy_nullable.sql +70 -0
  33. package/dist/drizzle/0216_drop_agent_loop_versions_goal_worker_policy.sql +81 -0
  34. package/dist/drizzle/0217_agents_capability_folder_dispatch.sql +12 -0
  35. package/dist/drizzle/0218_document_plates.sql +30 -0
  36. package/dist/drizzle/0219_document_section_waivers.sql +29 -0
  37. package/dist/drizzle/0220_artifacts_created_by_user.sql +21 -0
  38. package/dist/drizzle/0221_artifact_shares.sql +29 -0
  39. package/dist/drizzle/0221_workflow_interpreter_thin_slice.sql +71 -0
  40. package/dist/drizzle/0222_compliance_drainer_select_audit_events.sql +21 -0
  41. package/dist/drizzle/0223_document_conformance_reports.sql +38 -0
  42. package/dist/drizzle/0224_artifact_refresh_state.sql +11 -0
  43. package/dist/drizzle/0225_think216_agent_loop_convergence.sql +21 -0
  44. package/dist/drizzle/0226_drop_n8n_agent_step_runs.sql +6 -0
  45. package/dist/drizzle/0227_analyst_reader_role.sql +1055 -0
  46. package/dist/drizzle/0228_tenant_credentials_rds_iam_kind.sql +62 -0
  47. package/dist/drizzle/0229_analyst_reader_rds_iam_grant.sql +79 -0
  48. package/dist/drizzle/0230_analyst_rls.sql +1613 -0
  49. package/dist/drizzle/0231_cost_events_cache_write_enforcement_exempt.sql +11 -0
  50. package/dist/plugins/catalog/package.json +0 -1
  51. package/dist/plugins/catalog/src/__tests__/build-catalog.test.ts +0 -1
  52. package/dist/plugins/catalog/src/__tests__/catalog.test.ts +16 -8
  53. package/dist/plugins/catalog/src/__tests__/contracts.test.ts +56 -0
  54. package/dist/plugins/catalog/src/__tests__/plugin-package.test.ts +0 -2
  55. package/dist/plugins/catalog/src/__tests__/plugin-registry.test.ts +0 -5
  56. package/dist/plugins/catalog/src/contracts.ts +112 -0
  57. package/dist/plugins/catalog/src/registry/generated-first-party.ts +0 -6
  58. package/dist/plugins/n8n/README.md +4 -36
  59. package/dist/plugins/n8n/src/deployment/managed-app.ts +0 -17
  60. package/dist/plugins/n8n/src/manifest.ts +0 -5
  61. package/dist/plugins/n8n/terraform/n8n/main.tf +0 -26
  62. package/dist/plugins/n8n/terraform/n8n/outputs.tf +0 -5
  63. package/dist/plugins/n8n/terraform/n8n/variables.tf +0 -6
  64. package/dist/plugins/n8n/test/manifest.test.ts +0 -1
  65. package/dist/plugins/twenty/package.json +5 -0
  66. package/dist/plugins/twenty/scripts/lib/__tests__/load-records.test.ts +232 -0
  67. package/dist/plugins/twenty/scripts/lib/__tests__/mappers.test.ts +489 -0
  68. package/dist/plugins/twenty/scripts/lib/__tests__/members-ensure.test.ts +199 -0
  69. package/dist/plugins/twenty/scripts/lib/__tests__/provision-members-db.test.ts +43 -0
  70. package/dist/plugins/twenty/scripts/lib/__tests__/schema-ensure.test.ts +230 -0
  71. package/dist/plugins/twenty/scripts/lib/__tests__/twenty-client.test.ts +207 -0
  72. package/dist/plugins/twenty/scripts/lib/lastmile-reader.ts +282 -0
  73. package/dist/plugins/twenty/scripts/lib/load-attachments.ts +220 -0
  74. package/dist/plugins/twenty/scripts/lib/load-records.ts +680 -0
  75. package/dist/plugins/twenty/scripts/lib/mappers.ts +559 -0
  76. package/dist/plugins/twenty/scripts/lib/members-ensure.ts +381 -0
  77. package/dist/plugins/twenty/scripts/lib/provision-members-db.ts +191 -0
  78. package/dist/plugins/twenty/scripts/lib/schema-ensure.ts +425 -0
  79. package/dist/plugins/twenty/scripts/lib/twenty-client.ts +246 -0
  80. package/dist/plugins/twenty/scripts/migrate-lastmile.ts +610 -0
  81. package/dist/plugins/twenty/scripts/provision-twenty-members.ts +214 -0
  82. package/dist/plugins/twenty/src/manifest.ts +99 -0
  83. package/dist/plugins/twenty/terraform/twenty/README.md +1 -1
  84. package/dist/plugins/twenty/test/manifest.test.ts +12 -2
  85. package/dist/terraform/examples/greenfield/main.tf +90 -380
  86. package/dist/terraform/examples/greenfield/terraform.tfvars.example +0 -1
  87. package/dist/terraform/modules/app/agentcore-pi/README.md +1 -3
  88. package/dist/terraform/modules/app/agentcore-pi/main.tf +29 -38
  89. package/dist/terraform/modules/app/agentcore-pi/variables.tf +23 -15
  90. package/dist/terraform/modules/app/agentcore-runtime/main.tf +3 -3
  91. package/dist/terraform/modules/app/customer-domain/main.tf +16 -2
  92. package/dist/terraform/modules/app/deployment-control-plane/main.tf +5 -0
  93. package/dist/terraform/modules/app/deployment-control-plane/runner.py +85 -207
  94. package/dist/terraform/modules/app/deployment-control-plane/test_runner_bundle.py +56 -2
  95. package/dist/terraform/modules/app/hindsight-memory/main.tf +52 -4
  96. package/dist/terraform/modules/app/lambda-api/eval-fanout.tf +7 -0
  97. package/dist/terraform/modules/app/lambda-api/handlers.tf +493 -89
  98. package/dist/terraform/modules/app/lambda-api/iam-grouped.tf +215 -96
  99. package/dist/terraform/modules/app/lambda-api/main.tf +24 -5
  100. package/dist/terraform/modules/app/lambda-api/outputs.tf +19 -11
  101. package/dist/terraform/modules/app/lambda-api/variables.tf +94 -67
  102. package/dist/terraform/modules/app/ses-email/main.tf +14 -2
  103. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/main.tf +493 -0
  104. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/outputs.tf +34 -0
  105. package/dist/terraform/modules/app/workflow-interpreter-stepfunctions/variables.tf +32 -0
  106. package/dist/terraform/modules/data/aurora-postgres/main.tf +55 -0
  107. package/dist/terraform/modules/data/aurora-postgres/outputs.tf +15 -0
  108. package/dist/terraform/modules/data/s3-buckets/main.tf +26 -0
  109. package/dist/terraform/modules/thinkwork/README.md +0 -1
  110. package/dist/terraform/modules/thinkwork/main.tf +167 -446
  111. package/dist/terraform/modules/thinkwork/outputs.tf +2 -142
  112. package/dist/terraform/modules/thinkwork/variables.tf +55 -309
  113. package/dist/terraform/schema.graphql +4 -0
  114. package/dist/workspace-defaults/files/AGENTS.md +45 -31
  115. package/dist/workspace-defaults/files/CONTEXT.md +48 -4
  116. package/dist/workspace-defaults/files/GUARDRAILS.md +18 -0
  117. package/dist/workspace-defaults/files/MEMORY_GUIDE.md +32 -4
  118. package/dist/workspace-defaults/files/SPACE.md +34 -19
  119. package/dist/workspace-defaults/files/TOOLS.md +30 -0
  120. package/dist/workspace-defaults/files/skills/document-composer/SKILL.md +154 -0
  121. package/dist/workspace-defaults/files/skills/document-composer/references/authoring-rules.md +101 -0
  122. package/package.json +1 -1
  123. package/dist/plugins/company-brain/README.md +0 -37
  124. package/dist/plugins/company-brain/package.json +0 -33
  125. package/dist/plugins/company-brain/runtime/cognee/Dockerfile +0 -11
  126. package/dist/plugins/company-brain/smoke/cognee-managed-app-smoke.mjs +0 -458
  127. package/dist/plugins/company-brain/smoke/cognee-memory-cutover-smoke.mjs +0 -539
  128. package/dist/plugins/company-brain/smoke/company-brain-context-engine-smoke.mjs +0 -312
  129. package/dist/plugins/company-brain/smoke/company-brain-operations-smoke.mjs +0 -449
  130. package/dist/plugins/company-brain/smoke/company-brain-plugin-smoke.mjs +0 -655
  131. package/dist/plugins/company-brain/smoke/hindsight-memory-isolation-smoke.mjs +0 -474
  132. package/dist/plugins/company-brain/src/api/cognee-client.ts +0 -896
  133. package/dist/plugins/company-brain/src/api/cognee-cluster-identity.ts +0 -49
  134. package/dist/plugins/company-brain/src/api/cognee-memory-scope.ts +0 -84
  135. package/dist/plugins/company-brain/src/api/context-engine-provider.ts +0 -888
  136. package/dist/plugins/company-brain/src/api/migration.ts +0 -665
  137. package/dist/plugins/company-brain/src/deployment/cognee-managed-app.ts +0 -396
  138. package/dist/plugins/company-brain/src/index.ts +0 -62
  139. package/dist/plugins/company-brain/src/manifest.ts +0 -112
  140. package/dist/plugins/company-brain/terraform/cognee/README.md +0 -263
  141. package/dist/plugins/company-brain/terraform/cognee/main.tf +0 -766
  142. package/dist/plugins/company-brain/terraform/cognee/outputs.tf +0 -144
  143. package/dist/plugins/company-brain/terraform/cognee/variables.tf +0 -402
  144. package/dist/plugins/company-brain/test/api/cognee-client.test.ts +0 -562
  145. package/dist/plugins/company-brain/test/api/cognee-memory-scope.test.ts +0 -74
  146. package/dist/plugins/company-brain/test/api/context-engine-provider.test.ts +0 -447
  147. package/dist/plugins/company-brain/test/api/migration.test.ts +0 -465
  148. package/dist/plugins/company-brain/test/manifest.test.ts +0 -95
  149. package/dist/plugins/company-brain/tsconfig.json +0 -8
@@ -1,474 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Hindsight user + Space memory isolation smoke (THNK-83 U6).
4
- *
5
- * Dry-run is the default. Set SMOKE_ENABLE_HINDSIGHT_MEMORY_ISOLATION=1 to run
6
- * live against a deployed stage. Live mode writes unique user, Space A, and
7
- * Space B tokens through ThinkWork GraphQL, then verifies each search path only
8
- * returns the intended scope. It never calls raw Hindsight endpoints.
9
- *
10
- * Live env:
11
- * VITE_GRAPHQL_HTTP_URL or GRAPHQL_HTTP_URL or API_GRAPHQL_URL
12
- * API_AUTH_SECRET or THINKWORK_API_SECRET
13
- * SMOKE_TENANT_ID=<tenant uuid>
14
- * SMOKE_USER_ID=<users.id for user-memory capture/search>
15
- * SMOKE_SPACE_A_ID=<first authorized space id>
16
- * SMOKE_SPACE_B_ID=<second authorized space id>
17
- *
18
- * Optional:
19
- * SMOKE_UNAUTHORIZED_AUTH_TOKEN=<Cognito bearer for a user outside Space A>
20
- * SMOKE_REQUIRE_UNAUTHORIZED_CHECK=1
21
- * SMOKE_HINDSIGHT_MEMORY_RUN_ID=<stable smoke token>
22
- */
23
-
24
- import fs from "node:fs";
25
- import path from "node:path";
26
- import process from "node:process";
27
- import { attachSmokeEvidence } from "../../../scripts/smoke/deployment-evidence.mjs";
28
-
29
- const LIVE_ENABLED =
30
- process.env.SMOKE_ENABLE_HINDSIGHT_MEMORY_ISOLATION === "1";
31
- const REQUIRE_UNAUTHORIZED =
32
- process.env.SMOKE_REQUIRE_UNAUTHORIZED_CHECK === "1";
33
- const TIMEOUT_MS = Number(process.env.SMOKE_TIMEOUT_MS || 20_000);
34
- const env = { ...loadEnvFile(), ...process.env };
35
-
36
- const graphqlUrl = first(
37
- env.VITE_GRAPHQL_HTTP_URL,
38
- env.GRAPHQL_HTTP_URL,
39
- env.API_GRAPHQL_URL,
40
- );
41
- const apiSecret = first(env.API_AUTH_SECRET, env.THINKWORK_API_SECRET);
42
- const tenantId = first(env.SMOKE_TENANT_ID, env.TENANT_ID);
43
- const userId = first(env.SMOKE_USER_ID, env.SMOKE_ADMIN_USER_ID);
44
- const spaceAId = first(env.SMOKE_SPACE_A_ID, env.SMOKE_SPACE_ID);
45
- const spaceBId = first(env.SMOKE_SPACE_B_ID);
46
- const unauthorizedAuthToken = first(env.SMOKE_UNAUTHORIZED_AUTH_TOKEN);
47
- const runId = first(
48
- env.SMOKE_HINDSIGHT_MEMORY_RUN_ID,
49
- `tw-hindsight-isolation-${Date.now()}`,
50
- );
51
-
52
- const userToken = `${runId}-user-only`;
53
- const spaceAToken = `${runId}-space-a-only`;
54
- const spaceBToken = `${runId}-space-b-only`;
55
- const userMemoryText = `THNK-83 Hindsight isolation user memory ${userToken}: requester-only renewal preference.`;
56
- const spaceAMemoryText = `THNK-83 Hindsight isolation Space A memory ${spaceAToken}: alpha onboarding decision.`;
57
- const spaceBMemoryText = `THNK-83 Hindsight isolation Space B memory ${spaceBToken}: beta onboarding decision.`;
58
- const checks = [];
59
-
60
- if (!LIVE_ENABLED) {
61
- console.log(
62
- JSON.stringify(
63
- await attachSmokeEvidence(
64
- "hindsight-memory-isolation",
65
- {
66
- ok: true,
67
- skippedLive: true,
68
- reason:
69
- "set SMOKE_ENABLE_HINDSIGHT_MEMORY_ISOLATION=1 to run the deployed Hindsight user + Space memory isolation smoke",
70
- dryRun: {
71
- requiredWhenRunning: [
72
- "VITE_GRAPHQL_HTTP_URL or GRAPHQL_HTTP_URL or API_GRAPHQL_URL",
73
- "API_AUTH_SECRET or THINKWORK_API_SECRET",
74
- "SMOKE_TENANT_ID",
75
- "SMOKE_USER_ID",
76
- "SMOKE_SPACE_A_ID",
77
- "SMOKE_SPACE_B_ID",
78
- ],
79
- optionalChecks: [
80
- "SMOKE_UNAUTHORIZED_AUTH_TOKEN verifies a non-space user is rejected by the deployed auth path",
81
- "SMOKE_REQUIRE_UNAUTHORIZED_CHECK=1 makes the unauthorized token mandatory",
82
- "SMOKE_HINDSIGHT_MEMORY_RUN_ID reuses deterministic client capture ids for repeatable evidence",
83
- ],
84
- verifies: [
85
- "memorySystemConfig reports Hindsight-backed user + Space memory enabled",
86
- "captureMobileMemory writes user memory through ThinkWork GraphQL",
87
- "memorySearch recalls the user token and not Space A/B tokens",
88
- "captureSpaceMemory writes isolated Space A and Space B memories",
89
- "spaceMemorySearch for Space A recalls only the Space A token",
90
- "spaceMemorySearch for Space B recalls only the Space B token",
91
- "operator memoryRecords(scope: OPERATOR) search can inspect the same isolation tokens from /settings/memory",
92
- "an unauthorized Cognito caller cannot search Space A memory when a token is supplied",
93
- ],
94
- },
95
- },
96
- env,
97
- ),
98
- null,
99
- 2,
100
- ),
101
- );
102
- process.exit(0);
103
- }
104
-
105
- try {
106
- await runLiveSmoke();
107
- const failed = checks.filter((check) => !check.ok);
108
- console.log(
109
- JSON.stringify(
110
- await attachSmokeEvidence(
111
- "hindsight-memory-isolation",
112
- {
113
- ok: failed.length === 0,
114
- runId,
115
- userToken,
116
- spaceAToken,
117
- spaceBToken,
118
- checks,
119
- },
120
- env,
121
- ),
122
- null,
123
- 2,
124
- ),
125
- );
126
- process.exit(failed.length === 0 ? 0 : 1);
127
- } catch (error) {
128
- console.error(
129
- JSON.stringify(
130
- await attachSmokeEvidence(
131
- "hindsight-memory-isolation",
132
- {
133
- ok: false,
134
- runId,
135
- userToken,
136
- spaceAToken,
137
- spaceBToken,
138
- error: error instanceof Error ? error.message : String(error),
139
- checks,
140
- },
141
- env,
142
- ),
143
- null,
144
- 2,
145
- ),
146
- );
147
- process.exit(1);
148
- }
149
-
150
- async function runLiveSmoke() {
151
- requireEnv("VITE_GRAPHQL_HTTP_URL or GRAPHQL_HTTP_URL", graphqlUrl);
152
- requireEnv("API_AUTH_SECRET or THINKWORK_API_SECRET", apiSecret);
153
- requireEnv("SMOKE_TENANT_ID", tenantId);
154
- requireEnv("SMOKE_USER_ID", userId);
155
- requireEnv("SMOKE_SPACE_A_ID", spaceAId);
156
- requireEnv("SMOKE_SPACE_B_ID", spaceBId);
157
- if (spaceAId === spaceBId) {
158
- throw new Error("SMOKE_SPACE_A_ID and SMOKE_SPACE_B_ID must differ");
159
- }
160
-
161
- const { memorySystemConfig: config } = await gql(
162
- `query HindsightMemorySystemConfig {
163
- memorySystemConfig {
164
- activeEngine
165
- hindsightEnabled
166
- userMemoryEnabled
167
- spaceMemoryEnabled
168
- cogneeMemoryEnabled
169
- }
170
- }`,
171
- {},
172
- );
173
- assert(
174
- "Hindsight user and Space memory are active",
175
- config.hindsightEnabled &&
176
- config.userMemoryEnabled &&
177
- config.spaceMemoryEnabled,
178
- { config },
179
- );
180
-
181
- await captureUserMemory();
182
- await captureSpaceMemory(spaceAId, spaceAMemoryText, spaceAToken, "space-a");
183
- await captureSpaceMemory(spaceBId, spaceBMemoryText, spaceBToken, "space-b");
184
-
185
- const userSearch = await searchUserMemory(userToken);
186
- assertTokenIsolation("user memorySearch", userSearch, {
187
- expected: [userToken],
188
- forbidden: [spaceAToken, spaceBToken],
189
- });
190
-
191
- const spaceASearch = await searchSpaceMemory(spaceAId, spaceAToken);
192
- assertTokenIsolation("Space A spaceMemorySearch", spaceASearch, {
193
- expected: [spaceAToken],
194
- forbidden: [spaceBToken, userToken],
195
- });
196
-
197
- const spaceBSearch = await searchSpaceMemory(spaceBId, spaceBToken);
198
- assertTokenIsolation("Space B spaceMemorySearch", spaceBSearch, {
199
- expected: [spaceBToken],
200
- forbidden: [spaceAToken, userToken],
201
- });
202
-
203
- await assertOperatorTableFinds("operator table search finds user token", {
204
- query: userToken,
205
- expected: userToken,
206
- });
207
- await assertOperatorTableFinds("operator table search finds Space A token", {
208
- query: spaceAToken,
209
- expected: spaceAToken,
210
- });
211
- await assertOperatorTableFinds("operator table search finds Space B token", {
212
- query: spaceBToken,
213
- expected: spaceBToken,
214
- });
215
-
216
- await verifyUnauthorizedSpaceRead();
217
- }
218
-
219
- async function captureUserMemory() {
220
- const result = await gql(
221
- `mutation CaptureHindsightUserMemory($tenantId: ID!, $userId: ID!, $content: String!, $metadata: AWSJSON, $clientCaptureId: ID) {
222
- captureMobileMemory(
223
- tenantId: $tenantId
224
- userId: $userId
225
- content: $content
226
- metadata: $metadata
227
- clientCaptureId: $clientCaptureId
228
- ) {
229
- id
230
- content
231
- }
232
- }`,
233
- {
234
- tenantId,
235
- userId,
236
- content: userMemoryText,
237
- metadata: { smoke: "thnk-83", runId, token: userToken },
238
- clientCaptureId: `${runId}:user`,
239
- },
240
- );
241
- assert(
242
- "captureMobileMemory writes user isolation token",
243
- Boolean(result.captureMobileMemory?.id),
244
- {
245
- capture: result.captureMobileMemory,
246
- },
247
- );
248
- }
249
-
250
- async function captureSpaceMemory(spaceId, content, token, label) {
251
- const result = await gql(
252
- `mutation CaptureHindsightSpaceMemory($tenantId: ID!, $spaceId: ID!, $content: String!, $metadata: AWSJSON, $clientCaptureId: ID) {
253
- captureSpaceMemory(
254
- tenantId: $tenantId
255
- spaceId: $spaceId
256
- content: $content
257
- metadata: $metadata
258
- clientCaptureId: $clientCaptureId
259
- ) {
260
- memoryRecordId
261
- content { text }
262
- namespace
263
- }
264
- }`,
265
- {
266
- tenantId,
267
- spaceId,
268
- content,
269
- metadata: { smoke: "thnk-83", runId, token, label },
270
- clientCaptureId: `${runId}:${label}`,
271
- },
272
- );
273
- assert(
274
- `captureSpaceMemory writes ${label} isolation token`,
275
- Boolean(result.captureSpaceMemory?.memoryRecordId),
276
- {
277
- capture: result.captureSpaceMemory,
278
- },
279
- );
280
- }
281
-
282
- async function searchUserMemory(query) {
283
- return await gql(
284
- `query SearchHindsightUserMemory($tenantId: ID!, $userId: ID!, $query: String!, $limit: Int) {
285
- memorySearch(tenantId: $tenantId, userId: $userId, query: $query, limit: $limit) {
286
- totalCount
287
- records { memoryRecordId content { text } score namespace }
288
- }
289
- }`,
290
- { tenantId, userId, query, limit: 5 },
291
- );
292
- }
293
-
294
- async function searchSpaceMemory(spaceId, query) {
295
- return await gql(
296
- `query SearchHindsightSpaceMemory($tenantId: ID!, $spaceId: ID!, $query: String!, $limit: Int) {
297
- spaceMemorySearch(tenantId: $tenantId, spaceId: $spaceId, query: $query, limit: $limit) {
298
- totalCount
299
- records { memoryRecordId content { text } score namespace }
300
- }
301
- }`,
302
- { tenantId, spaceId, query, limit: 5 },
303
- );
304
- }
305
-
306
- async function assertOperatorTableFinds(name, { query, expected }) {
307
- const result = await gql(
308
- `query OperatorHindsightMemoryRecords($tenantId: ID!, $query: String!) {
309
- memoryRecords(
310
- tenantId: $tenantId
311
- namespace: "requester"
312
- scope: OPERATOR
313
- query: $query
314
- limit: 25
315
- ) {
316
- memoryRecordId
317
- content { text }
318
- bankId
319
- ownerType
320
- ownerId
321
- createdAt
322
- updatedAt
323
- }
324
- }`,
325
- { tenantId, query },
326
- );
327
- assert(name, containsToken(result, expected), {
328
- records: summarize(result.memoryRecords),
329
- });
330
- }
331
-
332
- async function verifyUnauthorizedSpaceRead() {
333
- if (!unauthorizedAuthToken) {
334
- const message =
335
- "set SMOKE_UNAUTHORIZED_AUTH_TOKEN to verify deployed Cognito space authorization";
336
- if (REQUIRE_UNAUTHORIZED) {
337
- assert("unauthorized Space A memory search is rejected", false, {
338
- message,
339
- });
340
- } else {
341
- skip("unauthorized Space A memory search is rejected", message);
342
- }
343
- return;
344
- }
345
-
346
- const response = await gqlRaw(
347
- `query UnauthorizedSpaceMemory($tenantId: ID!, $spaceId: ID!, $query: String!) {
348
- spaceMemorySearch(tenantId: $tenantId, spaceId: $spaceId, query: $query) {
349
- totalCount
350
- }
351
- }`,
352
- { tenantId, spaceId: spaceAId, query: spaceAToken },
353
- { bearerToken: unauthorizedAuthToken },
354
- );
355
- assert(
356
- "unauthorized Space A memory search is rejected",
357
- Array.isArray(response.errors) && response.errors.length > 0,
358
- { errors: response.errors ?? null },
359
- );
360
- }
361
-
362
- function assertTokenIsolation(name, value, { expected, forbidden }) {
363
- const details = { result: summarize(value), expected, forbidden };
364
- assert(
365
- `${name} contains expected token(s)`,
366
- expected.every((token) => containsToken(value, token)),
367
- details,
368
- );
369
- assert(
370
- `${name} excludes sibling scope token(s)`,
371
- forbidden.every((token) => !containsToken(value, token)),
372
- details,
373
- );
374
- }
375
-
376
- async function gql(query, variables) {
377
- const body = await gqlRaw(query, variables, { principalId: userId });
378
- if (body.errors?.length) {
379
- throw new Error(`GraphQL errors: ${JSON.stringify(body.errors)}`);
380
- }
381
- return body.data;
382
- }
383
-
384
- async function gqlRaw(query, variables, opts = {}) {
385
- const response = await fetchWithTimeout(graphqlUrl, {
386
- method: "POST",
387
- headers: {
388
- "content-type": "application/json",
389
- authorization: `Bearer ${opts.bearerToken ?? apiSecret}`,
390
- ...(opts.bearerToken
391
- ? { "x-tenant-id": tenantId }
392
- : {
393
- "x-tenant-id": tenantId,
394
- ...(opts.principalId ? { "x-principal-id": opts.principalId } : {}),
395
- }),
396
- },
397
- body: JSON.stringify({ query, variables }),
398
- });
399
- const text = await response.text();
400
- if (!response.ok) {
401
- if (opts.bearerToken) {
402
- return {
403
- errors: [
404
- {
405
- message: `GraphQL HTTP ${response.status}`,
406
- body: text.slice(0, 500),
407
- },
408
- ],
409
- };
410
- }
411
- throw new Error(`GraphQL HTTP ${response.status}: ${text.slice(0, 500)}`);
412
- }
413
- return JSON.parse(text);
414
- }
415
-
416
- async function fetchWithTimeout(url, options = {}) {
417
- const controller = new AbortController();
418
- const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
419
- try {
420
- return await fetch(url, { ...options, signal: controller.signal });
421
- } finally {
422
- clearTimeout(timer);
423
- }
424
- }
425
-
426
- function containsToken(value, token) {
427
- return JSON.stringify(value).toLowerCase().includes(token.toLowerCase());
428
- }
429
-
430
- function summarize(value) {
431
- const text = JSON.stringify(value);
432
- return text.length > 1200 ? `${text.slice(0, 1200)}...` : value;
433
- }
434
-
435
- function assert(name, ok, details = {}) {
436
- checks.push({ name, ok: Boolean(ok), ...details });
437
- if (!ok) throw new Error(`${name} failed`);
438
- }
439
-
440
- function skip(name, details) {
441
- checks.push({
442
- name,
443
- ok: true,
444
- skipped: true,
445
- ...(typeof details === "string" ? { reason: details } : details),
446
- });
447
- }
448
-
449
- function requireEnv(name, value) {
450
- if (!value) throw new Error(`${name} is required`);
451
- }
452
-
453
- function first(...values) {
454
- return values
455
- .find((value) => typeof value === "string" && value.trim())
456
- ?.trim();
457
- }
458
-
459
- function loadEnvFile() {
460
- const candidates = [
461
- path.resolve("apps/web/.env"),
462
- path.resolve("terraform/examples/greenfield/.env"),
463
- ];
464
- const output = {};
465
- for (const file of candidates) {
466
- if (!fs.existsSync(file)) continue;
467
- for (const line of fs.readFileSync(file, "utf8").split(/\r?\n/)) {
468
- const match = line.match(/^\s*([A-Z0-9_]+)\s*=\s*(.*)\s*$/);
469
- if (!match) continue;
470
- output[match[1]] = match[2].replace(/^['"]|['"]$/g, "");
471
- }
472
- }
473
- return output;
474
- }