create-flowdular 0.3.1 → 0.4.0

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 (92) hide show
  1. package/README.md +1 -1
  2. package/agent-template/.agents/skills/agent-tool-design/SKILL.md +25 -0
  3. package/agent-template/.agents/skills/integration-adapter/SKILL.md +116 -0
  4. package/agent-template/.agents/skills/module-new/SKILL.md +17 -2
  5. package/agent-template/.agents/skills/module-update/SKILL.md +14 -2
  6. package/agent-template/.agents/skills/release-eject-pr/SKILL.md +23 -26
  7. package/agent-template/.agents/skills/spec-interview/SKILL.md +16 -0
  8. package/agent-template/.agents/skills/translations-i18n/SKILL.md +2 -1
  9. package/agent-template/.agents/skills/ux-design/SKILL.md +4 -4
  10. package/agent-template/.ai/agents/sandbox/backend-engineer.md +9 -0
  11. package/agent-template/.ai/blueprints/agentic-module/README.md +15 -0
  12. package/agent-template/.ai/blueprints/agentic-module/allowed-paths.yaml +14 -0
  13. package/agent-template/.ai/blueprints/agentic-module/blueprint.json +19 -0
  14. package/agent-template/.ai/blueprints/agentic-module/gates.yaml +24 -0
  15. package/agent-template/.ai/blueprints/agentic-module/input.schema.json +18 -0
  16. package/agent-template/.ai/blueprints/agentic-module/plan.schema.json +35 -0
  17. package/agent-template/.ai/blueprints/agentic-module/required-files.yaml +28 -0
  18. package/agent-template/.ai/blueprints/agentic-module/spec-requirements.yaml +33 -0
  19. package/agent-template/.ai/blueprints/agentic-module/steps.yaml +68 -0
  20. package/agent-template/.ai/platform-capabilities.md +16 -11
  21. package/agent-template/.ai/references/catalog/migrations/0005_catalog_list_indexes.down.sql +3 -0
  22. package/agent-template/.ai/references/catalog/migrations/0005_catalog_list_indexes.up.sql +11 -0
  23. package/agent-template/.ai/references/catalog/module.json +12 -2
  24. package/agent-template/.ai/references/catalog/package.json +2 -2
  25. package/agent-template/.ai/references/catalog/spec/module.yaml +26 -5
  26. package/agent-template/.ai/references/catalog/src/agent/tools.ts +19 -10
  27. package/agent-template/.ai/references/catalog/src/api/endpoints.ts +150 -10
  28. package/agent-template/.ai/references/catalog/src/api/list-cursor.ts +83 -0
  29. package/agent-template/.ai/references/catalog/src/client/CatalogView.tsrx +505 -159
  30. package/agent-template/.ai/references/catalog/src/client/api.ts +124 -36
  31. package/agent-template/.ai/references/catalog/src/client/contribution.tsrx +5 -0
  32. package/agent-template/.ai/references/catalog/src/client/state.ts +169 -3
  33. package/agent-template/.ai/references/catalog/src/domain/lists.ts +7 -0
  34. package/agent-template/.ai/references/catalog/src/domain/types.ts +20 -0
  35. package/agent-template/.ai/references/catalog/src/platform.ts +20 -0
  36. package/agent-template/.ai/references/catalog/src/services/catalog-service.ts +143 -8
  37. package/agent-template/.ai/references/catalog/src/services/database-repository.ts +104 -17
  38. package/agent-template/.ai/references/catalog/src/services/item-export.ts +81 -0
  39. package/agent-template/.ai/references/catalog/src/services/migration.ts +27 -1
  40. package/agent-template/.ai/references/catalog/src/services/repository.ts +31 -2
  41. package/agent-template/.ai/references/catalog/tests/agent-tools.test.ts +6 -5
  42. package/agent-template/.ai/references/catalog/tests/client-state.test.ts +124 -0
  43. package/agent-template/.ai/references/catalog/tests/endpoints.test.ts +269 -0
  44. package/agent-template/.ai/references/catalog/tests/export.test.ts +134 -0
  45. package/agent-template/.ai/references/catalog/tests/idempotency.test.ts +15 -14
  46. package/agent-template/.ai/references/catalog/tests/list.test.ts +217 -0
  47. package/agent-template/.ai/references/catalog/tests/migrations.test.ts +58 -2
  48. package/agent-template/.ai/references/catalog/tests/module.test.ts +2 -1
  49. package/agent-template/.ai/references/catalog/tests/support/database.ts +14 -0
  50. package/agent-template/.ai/references/catalog/translations/en.json +35 -4
  51. package/agent-template/.ai/references/catalog/translations/pl.json +35 -4
  52. package/agent-template/.ai/references/catalog.provenance.json +34 -26
  53. package/agent-template/.ai/rules/flowdular.md +2 -1
  54. package/agent-template/.ai/skills/README.md +1 -0
  55. package/agent-template/.ai/skills/agent-tool-design/SKILL.md +25 -0
  56. package/agent-template/.ai/skills/integration-adapter/SKILL.md +121 -0
  57. package/agent-template/.ai/skills/module-new/SKILL.md +17 -2
  58. package/agent-template/.ai/skills/module-update/SKILL.md +14 -2
  59. package/agent-template/.ai/skills/release-eject-pr/SKILL.md +23 -26
  60. package/agent-template/.ai/skills/spec-interview/SKILL.md +16 -0
  61. package/agent-template/.ai/skills/translations-i18n/SKILL.md +2 -1
  62. package/agent-template/.ai/skills/ux-design/SKILL.md +4 -4
  63. package/agent-template/.claude/skills/agent-tool-design/SKILL.md +25 -0
  64. package/agent-template/.claude/skills/integration-adapter/SKILL.md +116 -0
  65. package/agent-template/.claude/skills/module-new/SKILL.md +17 -2
  66. package/agent-template/.claude/skills/module-update/SKILL.md +14 -2
  67. package/agent-template/.claude/skills/release-eject-pr/SKILL.md +23 -26
  68. package/agent-template/.claude/skills/spec-interview/SKILL.md +16 -0
  69. package/agent-template/.claude/skills/translations-i18n/SKILL.md +2 -1
  70. package/agent-template/.claude/skills/ux-design/SKILL.md +4 -4
  71. package/agent-template/AGENTS.md +2 -1
  72. package/agent-template/CLAUDE.md +2 -1
  73. package/agent-template/docs/agent-contract.md +1 -1
  74. package/agent-template/docs/cli.md +8 -3
  75. package/agent-template/docs/configuration.md +29 -0
  76. package/agent-template/docs/design-system.md +112 -13
  77. package/agent-template/docs/module-distribution.md +10 -3
  78. package/agent-template/docs/modules.md +51 -1
  79. package/agent-template/docs/operations.md +2 -0
  80. package/agent-template/docs/sandbox.md +76 -1
  81. package/assets/flowdular-banner.webp +0 -0
  82. package/package.json +1 -1
  83. package/template/default/flowdular.json +2 -0
  84. package/template/default/modules/example/package.json +1 -1
  85. package/template/default/modules/example/src/client/NotesView.tsrx +12 -16
  86. package/template/default/modules/example/tests/module.test.ts +3 -2
  87. package/template/default/modules/example/translations/pl.json +3 -1
  88. package/template/default/package.json +1 -1
  89. package/template/default/platform/package.json +1 -1
  90. package/template/default/platform/src/generated/modules.client.ts +4 -0
  91. package/template/default/platform/src/generated/modules.server.ts +68 -8
  92. package/assets/flowdular-banner.png +0 -0
@@ -65,6 +65,79 @@ so a workspace can change them.
65
65
  When the work is done, eject it into `modules/` and enable it, or open a pull
66
66
  request with the gate evidence attached.
67
67
 
68
+ ## Sample data and recorded adapters
69
+
70
+ A session can carry a sample of the data the module will really see: attach a
71
+ CSV, JSON or plain text file (`.csv`, `.json`, `.txt`) to the brief or the
72
+ composer, within the attachment limits (5 MB per file, 10 per session). The file
73
+ stays in the session directory, is never sent to the connected application, and
74
+ is deleted with the session.
75
+
76
+ Every turn reads the sample through the read-only sandbox tool `sample-data`.
77
+ Without input it lists each sample file with its columns, row count and a
78
+ parsed preview of the first 20 rows; `{ "name": "customers.csv" }` previews one
79
+ file. CSV is parsed with quoted fields and a detected `,`, `;` or tab delimiter,
80
+ JSON from a top-level array or the first array inside an object, text as lines.
81
+ A value is cut at 200 characters and a row at 40 columns; one preview stays
82
+ under 32 KB and the listing under 64 KB, listing a file that does not fit
83
+ without its rows. The BYOK driver offers the tool to the model. The local
84
+ `claude` and `codex` drivers have no tool channel, so the same listing is
85
+ written to `reference/sample-data.json` for the turn.
86
+
87
+ The backend engineer derives the module's fixtures from the sample:
88
+ `tests/fixtures/*.json` for the tests and `preview/seed.json` for the preview. It
89
+ keeps the shape and replaces real names, contacts and identifiers with invented
90
+ values, because fixtures ship with the module. The role may write `preview/**`,
91
+ `src/preview.ts`, `research-fixtures.json` and `adapters/**`.
92
+
93
+ ### Seeding the preview
94
+
95
+ When a draft module has both `preview/seed.json` (at most 1 MB) and
96
+ `src/preview.ts` exporting `seed`, the preview calls it once the generation has
97
+ started:
98
+
99
+ ```ts
100
+ export async function seed(context: {
101
+ readonly tenantId: string; // the preview workspace
102
+ readonly accountId: string; // the preview account
103
+ readonly data: unknown; // parsed preview/seed.json
104
+ readonly databases: DatabaseProvider; // the preview's own provider
105
+ }): Promise<void>;
106
+ ```
107
+
108
+ `seed()` writes through the module's own repository, as the server composition
109
+ does. The sandbox keeps a hash of both files in the session data directory and
110
+ calls `seed()` again only when one of them changes, so it must be idempotent
111
+ (upsert by the natural key). A failure is reported as the preview error of that
112
+ module and never stops the preview. The preview does not seed through an
113
+ `import.ports.v1` port: only `import.core` can read its port registry (the
114
+ public capability registers ports), a port write needs a full principal and a
115
+ job, and most drafts do not compose `import.core`.
116
+
117
+ ### Recorded adapters
118
+
119
+ A draft spec with a `research` section previews through `research.core`. The
120
+ preview composes it from the platform modules ahead of the drafts and holds
121
+ `research.core.adapter` at `recorded`, the adapter chain at the recorded adapter
122
+ alone (`searchOrder` at `recorded` with `recordedEnabled` on, `fetchOrder` at
123
+ `direct`) and `research.core.recordedFixturesPath` at the absolute path of the
124
+ module's `research-fixtures.json` for every workspace. A chain holding the
125
+ recorded adapter reads pages from the same file before it looks at the fetch
126
+ order, so nothing reaches the network. Changing any of them answers
127
+ `409 SANDBOX_LIVE_ADAPTER_REFUSED`. When several drafts declare research, the
128
+ first one in session order supplies the path. An entry of the spec's `adapters`
129
+ section reads the fixture its `recorded` field names, by convention
130
+ `adapters/<id>.recorded.json`.
131
+
132
+ A session declares only recorded adapters; an owner connects a live search or
133
+ connector instance after delivery. When a draft spec sets `research.adapter` to
134
+ `model-native`, `searxng`, `firecrawl` or `connector`, or lists an adapter
135
+ without `recorded`, the `spec-schema` gate fails with
136
+ `SANDBOX_LIVE_ADAPTER_REFUSED` and names the file and field, so the turn goes
137
+ back to its specialist and delivery stops. The preview refuses to compose such a
138
+ session with the same code before a worker starts. A `research` section without
139
+ `adapter` is previewed on the recorded fixtures.
140
+
68
141
  ## Deliver as a pull request
69
142
 
70
143
  The eject route (`POST /sandbox/api/sessions/:id/eject`) takes `target:
@@ -81,7 +154,9 @@ request time (`delivery/configuration.ts`): `targets`, `default`,
81
154
  `maxChangedFiles` and `git` with `remote`
82
155
  (`origin`), `repository` (`owner/name`, derived from the remote when null),
83
156
  `baseBranch` (`main`), `branchPrefix` (`sandbox`), `provider` (`github` or
84
- `none`), `mode` (`auto`, `direct` or `fork`), `forkOwner` and `reviewers`.
157
+ `none`), `mode` (`auto`, `direct` or `fork`), `forkOwner`, `reviewers` and
158
+ `labels` (added to the pull request after creation, default
159
+ `sandbox-delivery`; a label the repository lacks never fails a delivery).
85
160
 
86
161
  Operator settings live in the sandbox configuration
87
162
  (`.flowdular/sandbox/config.json`, `GitHubDeliveryConfiguration` in
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-flowdular",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Scaffold a Flowdular application: the platform, one example module and the secrets a fresh install needs.",
6
6
  "license": "MIT",
@@ -12,6 +12,7 @@
12
12
  "system.core",
13
13
  "auth.core",
14
14
  "access.core",
15
+ "adapters.core",
15
16
  "reports.core",
16
17
  "metering.core",
17
18
  "agents.core",
@@ -26,6 +27,7 @@
26
27
  "import.core",
27
28
  "notifications.core",
28
29
  "profile.core",
30
+ "research.core",
29
31
  "sandbox.core",
30
32
  "search.core",
31
33
  "users.core",
@@ -16,7 +16,7 @@
16
16
  "dependencies": {
17
17
  "octane": "0.1.51",
18
18
  "segment-state": "0.2.1",
19
- "@flowdular/sdk": "0.3.1"
19
+ "@flowdular/sdk": "0.4.0"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@tsrx/typescript-plugin": "0.3.120",
@@ -2,6 +2,9 @@ import { useEffect, useMemo } from 'octane';
2
2
  import {
3
3
  Alert,
4
4
  Button,
5
+ CellStack,
6
+ CellText,
7
+ CellTime,
5
8
  Drawer,
6
9
  FormField,
7
10
  Icon,
@@ -31,20 +34,18 @@ function columns(): readonly TableColumn<Note>[] {
31
34
  {
32
35
  key: 'note',
33
36
  header: t('example.table.column.note'),
34
- width: '70%',
35
- cell: (note) => <span class="ui-cell">
36
- <b>{note.title}</b>
37
- <span class="ui-cell__muted">{note.body}</span>
38
- </span>,
37
+ width: 'auto',
38
+ cell: (note) =>
39
+ <CellStack
40
+ primary={note.title}
41
+ secondary={<CellText value={note.body} muted />}
42
+ />,
39
43
  },
40
44
  {
41
45
  key: 'created',
42
46
  header: t('example.table.column.created'),
43
- width: '30%',
44
- cell: (note) => new Intl.DateTimeFormat(activeLocale(), {
45
- dateStyle: 'medium',
46
- timeStyle: 'short',
47
- }).format(new Date(note.createdAt)),
47
+ width: '170px',
48
+ cell: (note) => <CellTime value={note.createdAt} />,
48
49
  },
49
50
  ];
50
51
  }
@@ -152,12 +153,7 @@ export function NotesView(props: NotesViewProps) @{
152
153
  }
153
154
  <TableCard
154
155
  title={t('example.table.title')}
155
- count={t(
156
- notes.length === 1
157
- ? 'example.table.count.one'
158
- : 'example.table.count.other',
159
- { count: notes.length },
160
- )}
156
+ count={t('example.table.count', { count: notes.length })}
161
157
  search={<SearchField
162
158
  value={query}
163
159
  placeholder={t('example.table.search.placeholder')}
@@ -1,5 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { describe, expect, it } from 'vitest';
3
+ import { translationKeys } from '@flowdular/sdk/contracts';
3
4
  import manifest from '../module.json' with { type: 'json' };
4
5
  import translationsEn from '../translations/en.json' with { type: 'json' };
5
6
  import translationsPl from '../translations/pl.json' with { type: 'json' };
@@ -28,8 +29,8 @@ describe('example.core manifest', () => {
28
29
 
29
30
  describe('example.core translations', () => {
30
31
  it('ships the same key set in every declared locale', () => {
31
- expect(Object.keys(translationsPl).sort()).toEqual(
32
- Object.keys(translationsEn).sort(),
32
+ expect(translationKeys(translationsPl)).toEqual(
33
+ translationKeys(translationsEn),
33
34
  );
34
35
  expect(manifest.locales).toEqual(['en', 'pl']);
35
36
  });
@@ -9,7 +9,9 @@
9
9
  "page.action.new": "Nowa notatka",
10
10
  "table.title": "Notatki",
11
11
  "table.count.one": "{count} notatka",
12
- "table.count.other": "{count} notatek",
12
+ "table.count.few": "{count} notatki",
13
+ "table.count.many": "{count} notatek",
14
+ "table.count.other": "{count} notatki",
13
15
  "table.column.note": "Notatka",
14
16
  "table.column.created": "Utworzono",
15
17
  "table.search.placeholder": "Szukaj notatek",
@@ -25,7 +25,7 @@
25
25
  "devDependencies": {
26
26
  "@tsrx/prettier-plugin": "0.3.120",
27
27
  "prettier": "3.6.2",
28
- "flowdular": "0.3.1",
28
+ "flowdular": "0.4.0",
29
29
  "rulesync": "16.21.0"
30
30
  }
31
31
  }
@@ -15,7 +15,7 @@
15
15
  "@octanejs/vite-plugin": "0.1.51",
16
16
  "octane": "0.1.51",
17
17
  "pg": "8.23.0",
18
- "@flowdular/sdk": "0.3.1"
18
+ "@flowdular/sdk": "0.4.0"
19
19
  },
20
20
  "devDependencies": {
21
21
  "@octanejs/app-core": "0.0.47",
@@ -8,6 +8,7 @@ import type {
8
8
  import { createClientContribution as system_core } from '@flowdular/sdk/modules/system/client';
9
9
  import { createClientContribution as auth_core } from '@flowdular/sdk/modules/auth/client';
10
10
  import { createClientContribution as access_core } from '@flowdular/sdk/modules/access/client';
11
+ import { createClientContribution as adapters_core } from '@flowdular/sdk/modules/adapters/client';
11
12
  import { createClientContribution as reports_core } from '@flowdular/sdk/modules/reports/client';
12
13
  import { createClientContribution as metering_core } from '@flowdular/sdk/modules/metering/client';
13
14
  import { createClientContribution as agents_core } from '@flowdular/sdk/modules/agents/client';
@@ -23,6 +24,7 @@ import { createClientContribution as exports_core } from '@flowdular/sdk/modules
23
24
  import { createClientContribution as import_core } from '@flowdular/sdk/modules/import/client';
24
25
  import { createClientContribution as notifications_core } from '@flowdular/sdk/modules/notifications/client';
25
26
  import { createClientContribution as profile_core } from '@flowdular/sdk/modules/profile/client';
27
+ import { createClientContribution as research_core } from '@flowdular/sdk/modules/research/client';
26
28
  import { createClientContribution as sandbox_core } from '@flowdular/sdk/modules/sandbox/client';
27
29
  import { createClientContribution as search_core } from '@flowdular/sdk/modules/search/client';
28
30
  import { createClientContribution as users_core } from '@flowdular/sdk/modules/users/client';
@@ -34,6 +36,7 @@ export function createModuleClientContributions(
34
36
  system_core(context),
35
37
  auth_core(context),
36
38
  access_core(context),
39
+ adapters_core(context),
37
40
  reports_core(context),
38
41
  metering_core(context),
39
42
  agents_core(context),
@@ -49,6 +52,7 @@ export function createModuleClientContributions(
49
52
  import_core(context),
50
53
  notifications_core(context),
51
54
  profile_core(context),
55
+ research_core(context),
52
56
  sandbox_core(context),
53
57
  search_core(context),
54
58
  users_core(context),
@@ -5,9 +5,13 @@ import type {
5
5
  PlatformServerContext,
6
6
  } from '@flowdular/sdk/modules/auth/server';
7
7
  import type { WebMount } from '@flowdular/sdk/server';
8
- import { createModuleMetrics } from '@flowdular/sdk/server';
8
+ import {
9
+ bindModuleCompositions,
10
+ createModuleMetrics,
11
+ } from '@flowdular/sdk/server';
9
12
  import { createServerComposition as system_core } from '@flowdular/sdk/modules/system/platform';
10
13
  import { createServerComposition as access_core } from '@flowdular/sdk/modules/access/platform';
14
+ import { createServerComposition as adapters_core } from '@flowdular/sdk/modules/adapters/platform';
11
15
  import { createServerComposition as reports_core } from '@flowdular/sdk/modules/reports/platform';
12
16
  import { createServerComposition as metering_core } from '@flowdular/sdk/modules/metering/platform';
13
17
  import { createServerComposition as agents_core } from '@flowdular/sdk/modules/agents/platform';
@@ -23,6 +27,7 @@ import { createServerComposition as exports_core } from '@flowdular/sdk/modules/
23
27
  import { createServerComposition as import_core } from '@flowdular/sdk/modules/import/platform';
24
28
  import { createServerComposition as notifications_core } from '@flowdular/sdk/modules/notifications/platform';
25
29
  import { createServerComposition as profile_core } from '@flowdular/sdk/modules/profile/platform';
30
+ import { createServerComposition as research_core } from '@flowdular/sdk/modules/research/platform';
26
31
  import { createServerComposition as sandbox_core } from '@flowdular/sdk/modules/sandbox/platform';
27
32
  import { createServerComposition as search_core } from '@flowdular/sdk/modules/search/platform';
28
33
  import { createServerComposition as users_core } from '@flowdular/sdk/modules/users/platform';
@@ -30,14 +35,14 @@ import { createServerComposition as users_core } from '@flowdular/sdk/modules/us
30
35
  export function composeModuleServer(
31
36
  context: PlatformServerContext,
32
37
  ): readonly PlatformServerComposition[] {
33
- return [
38
+ return bindModuleCompositions([
34
39
  {
35
40
  ...system_core({
36
41
  ...context,
37
42
  agentDefinitions: context.agentDefinitions.forModule('system.core'),
38
43
  dataClasses: context.dataClasses.forModule('system.core'),
39
44
  capabilities: context.capabilities.forModule('system.core', {
40
- provides: [],
45
+ provides: ['system.modules.v1'],
41
46
  requires: [],
42
47
  }),
43
48
  metrics: createModuleMetrics('system.core'),
@@ -57,6 +62,26 @@ export function composeModuleServer(
57
62
  }),
58
63
  moduleId: 'access.core',
59
64
  },
65
+ {
66
+ ...adapters_core({
67
+ ...context,
68
+ agentDefinitions: context.agentDefinitions.forModule('adapters.core'),
69
+ dataClasses: context.dataClasses.forModule('adapters.core'),
70
+ capabilities: context.capabilities.forModule('adapters.core', {
71
+ provides: ['adapters.sources.v1', 'adapters.sinks.v1'],
72
+ requires: [
73
+ { id: 'connectors.calls.v1', optional: true },
74
+ { id: 'connectors.instances.v1', optional: true },
75
+ { id: 'import.ports.v1', optional: true },
76
+ { id: 'import.write.v1', optional: true },
77
+ { id: 'exports.lists.v1', optional: true },
78
+ { id: 'metering.meters.v1', optional: true },
79
+ ],
80
+ }),
81
+ metrics: createModuleMetrics('adapters.core'),
82
+ }),
83
+ moduleId: 'adapters.core',
84
+ },
60
85
  {
61
86
  ...reports_core({
62
87
  ...context,
@@ -174,7 +199,12 @@ export function composeModuleServer(
174
199
  agentDefinitions: context.agentDefinitions.forModule('connectors.core'),
175
200
  dataClasses: context.dataClasses.forModule('connectors.core'),
176
201
  capabilities: context.capabilities.forModule('connectors.core', {
177
- provides: ['connectors.definitions.v1', 'connectors.calls.v1'],
202
+ provides: [
203
+ 'connectors.definitions.v1',
204
+ 'connectors.calls.v1',
205
+ 'connectors.egress.v1',
206
+ 'connectors.instances.v1',
207
+ ],
178
208
  requires: [],
179
209
  }),
180
210
  metrics: createModuleMetrics('connectors.core'),
@@ -200,8 +230,12 @@ export function composeModuleServer(
200
230
  agentDefinitions: context.agentDefinitions.forModule('documents.core'),
201
231
  dataClasses: context.dataClasses.forModule('documents.core'),
202
232
  capabilities: context.capabilities.forModule('documents.core', {
203
- provides: ['documents.attachments.v1'],
204
- requires: [],
233
+ provides: [
234
+ 'documents.attachments.v1',
235
+ 'documents.text.v1',
236
+ 'documents.templates.v1',
237
+ ],
238
+ requires: [{ id: 'connectors.egress.v1', optional: true }],
205
239
  }),
206
240
  metrics: createModuleMetrics('documents.core'),
207
241
  }),
@@ -239,7 +273,7 @@ export function composeModuleServer(
239
273
  agentDefinitions: context.agentDefinitions.forModule('import.core'),
240
274
  dataClasses: context.dataClasses.forModule('import.core'),
241
275
  capabilities: context.capabilities.forModule('import.core', {
242
- provides: ['import.ports.v1'],
276
+ provides: ['import.ports.v1', 'import.write.v1'],
243
277
  requires: [{ id: 'documents.attachments.v1' }],
244
278
  }),
245
279
  metrics: createModuleMetrics('import.core'),
@@ -273,6 +307,32 @@ export function composeModuleServer(
273
307
  }),
274
308
  moduleId: 'profile.core',
275
309
  },
310
+ {
311
+ ...research_core({
312
+ ...context,
313
+ agentDefinitions: context.agentDefinitions.forModule('research.core'),
314
+ dataClasses: context.dataClasses.forModule('research.core'),
315
+ capabilities: context.capabilities.forModule('research.core', {
316
+ provides: [
317
+ 'research.search.v1',
318
+ 'research.fetch.v1',
319
+ 'research.evidence.v1',
320
+ ],
321
+ requires: [
322
+ { id: 'connectors.calls.v1', optional: true },
323
+ { id: 'connectors.definitions.v1', optional: true },
324
+ { id: 'connectors.instances.v1', optional: true },
325
+ { id: 'connectors.egress.v1', optional: true },
326
+ { id: 'metering.meters.v1', optional: true },
327
+ { id: 'documents.attachments.v1', optional: true },
328
+ { id: 'documents.text.v1', optional: true },
329
+ { id: 'exports.lists.v1', optional: true },
330
+ ],
331
+ }),
332
+ metrics: createModuleMetrics('research.core'),
333
+ }),
334
+ moduleId: 'research.core',
335
+ },
276
336
  {
277
337
  ...sandbox_core({
278
338
  ...context,
@@ -316,7 +376,7 @@ export function composeModuleServer(
316
376
  }),
317
377
  moduleId: 'users.core',
318
378
  },
319
- ];
379
+ ]);
320
380
  }
321
381
 
322
382
  export const moduleWebMounts: readonly WebMount[] = [] as const;
Binary file