create-flowdular 0.3.2 → 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 (61) hide show
  1. package/agent-template/.agents/skills/agent-tool-design/SKILL.md +25 -0
  2. package/agent-template/.agents/skills/integration-adapter/SKILL.md +116 -0
  3. package/agent-template/.agents/skills/module-new/SKILL.md +17 -2
  4. package/agent-template/.agents/skills/module-update/SKILL.md +14 -2
  5. package/agent-template/.agents/skills/release-eject-pr/SKILL.md +23 -26
  6. package/agent-template/.agents/skills/spec-interview/SKILL.md +16 -0
  7. package/agent-template/.agents/skills/translations-i18n/SKILL.md +2 -1
  8. package/agent-template/.agents/skills/ux-design/SKILL.md +4 -4
  9. package/agent-template/.ai/agents/sandbox/backend-engineer.md +9 -0
  10. package/agent-template/.ai/blueprints/agentic-module/README.md +15 -0
  11. package/agent-template/.ai/blueprints/agentic-module/allowed-paths.yaml +14 -0
  12. package/agent-template/.ai/blueprints/agentic-module/blueprint.json +19 -0
  13. package/agent-template/.ai/blueprints/agentic-module/gates.yaml +24 -0
  14. package/agent-template/.ai/blueprints/agentic-module/input.schema.json +18 -0
  15. package/agent-template/.ai/blueprints/agentic-module/plan.schema.json +35 -0
  16. package/agent-template/.ai/blueprints/agentic-module/required-files.yaml +28 -0
  17. package/agent-template/.ai/blueprints/agentic-module/spec-requirements.yaml +33 -0
  18. package/agent-template/.ai/blueprints/agentic-module/steps.yaml +68 -0
  19. package/agent-template/.ai/platform-capabilities.md +14 -11
  20. package/agent-template/.ai/references/catalog/module.json +2 -2
  21. package/agent-template/.ai/references/catalog/package.json +2 -2
  22. package/agent-template/.ai/references/catalog/spec/module.yaml +2 -2
  23. package/agent-template/.ai/references/catalog.provenance.json +6 -6
  24. package/agent-template/.ai/rules/flowdular.md +2 -1
  25. package/agent-template/.ai/skills/README.md +1 -0
  26. package/agent-template/.ai/skills/agent-tool-design/SKILL.md +25 -0
  27. package/agent-template/.ai/skills/integration-adapter/SKILL.md +121 -0
  28. package/agent-template/.ai/skills/module-new/SKILL.md +17 -2
  29. package/agent-template/.ai/skills/module-update/SKILL.md +14 -2
  30. package/agent-template/.ai/skills/release-eject-pr/SKILL.md +23 -26
  31. package/agent-template/.ai/skills/spec-interview/SKILL.md +16 -0
  32. package/agent-template/.ai/skills/translations-i18n/SKILL.md +2 -1
  33. package/agent-template/.ai/skills/ux-design/SKILL.md +4 -4
  34. package/agent-template/.claude/skills/agent-tool-design/SKILL.md +25 -0
  35. package/agent-template/.claude/skills/integration-adapter/SKILL.md +116 -0
  36. package/agent-template/.claude/skills/module-new/SKILL.md +17 -2
  37. package/agent-template/.claude/skills/module-update/SKILL.md +14 -2
  38. package/agent-template/.claude/skills/release-eject-pr/SKILL.md +23 -26
  39. package/agent-template/.claude/skills/spec-interview/SKILL.md +16 -0
  40. package/agent-template/.claude/skills/translations-i18n/SKILL.md +2 -1
  41. package/agent-template/.claude/skills/ux-design/SKILL.md +4 -4
  42. package/agent-template/AGENTS.md +2 -1
  43. package/agent-template/CLAUDE.md +2 -1
  44. package/agent-template/docs/agent-contract.md +1 -1
  45. package/agent-template/docs/cli.md +8 -3
  46. package/agent-template/docs/configuration.md +29 -0
  47. package/agent-template/docs/design-system.md +112 -13
  48. package/agent-template/docs/module-distribution.md +10 -3
  49. package/agent-template/docs/modules.md +38 -1
  50. package/agent-template/docs/operations.md +2 -0
  51. package/agent-template/docs/sandbox.md +76 -1
  52. package/package.json +1 -1
  53. package/template/default/flowdular.json +2 -0
  54. package/template/default/modules/example/package.json +1 -1
  55. package/template/default/modules/example/src/client/NotesView.tsrx +12 -16
  56. package/template/default/modules/example/tests/module.test.ts +3 -2
  57. package/template/default/modules/example/translations/pl.json +3 -1
  58. package/template/default/package.json +1 -1
  59. package/template/default/platform/package.json +1 -1
  60. package/template/default/platform/src/generated/modules.client.ts +4 -0
  61. package/template/default/platform/src/generated/modules.server.ts +61 -4
@@ -39,6 +39,18 @@ spec instead of scanning the repository. Every section is optional and a
39
39
  `agentTools[]`: `id`, `permission`, `description`, `risk`.
40
40
  - `outOfScope[]` records what is deliberately not built; `decisions[]` records
41
41
  each interview question, its answer and whether a user or a default decided.
42
+ - `research`: `adapter` (`model-native`, `searxng`, `firecrawl`, `connector`
43
+ or `recorded`),
44
+ `allowDomains`, `denyDomains`, `monthlyQueryBudget`, and `evidenceOwner`, the
45
+ entity whose records evidence attaches to through `research.core`.
46
+ - `adapters[]`: `id` (prefixed with the module id), `direction` (`source` or
47
+ `sink`), `connector` and `operation` (a `connectors.core` definition and
48
+ operation key), `port` (an import port id for a source, a list export id for a
49
+ sink), `schedule` (a five-field cron or `null`), `mapping[]` (`from`, `to`,
50
+ `transform` of `rename`, `constant`, `format` or `lookup`, `value`) and
51
+ `recorded` (`adapters/<name>.recorded.json`).
52
+ - `templates[]`: `id`, `title`, `inputEntity`, `format` (`pdf` or `docx`) and
53
+ `body` (`templates/<name>.md`).
42
54
 
43
55
  Validation is more than the schema: an action permission must exist in
44
56
  `permissions`, every `entity` must name an entity, screen `columns` and
@@ -53,6 +65,19 @@ at least one entity (`SPEC_ACTION_PERMISSION_UNKNOWN`, `SPEC_ENTITY_UNKNOWN`,
53
65
  `SPEC_DUPLICATE_ID`). A client without a list screen, or a stored entity with no
54
66
  tenant-unique field, is a warning.
55
67
 
68
+ The three optional sections have checks of their own. `research.evidenceOwner`
69
+ and `templates[].inputEntity` must name an entity (`SPEC_ENTITY_UNKNOWN`). An
70
+ adapter id must start with the module id (`SPEC_ADAPTER_ID_NAMESPACE`); a source
71
+ adapter's `port` must belong to this module or a declared dependency
72
+ (`SPEC_ADAPTER_PORT_UNKNOWN`); `schedule` must be a cron `automations.core`
73
+ accepts: minute, hour, day of month, month and day of week, each `*`, a number,
74
+ a three letter month or weekday name, a list, a range or a step
75
+ (`SPEC_ADAPTER_SCHEDULE_INVALID`); and a mapping needs `from` unless it is a
76
+ `constant` and `value` unless it is a `rename` (`SPEC_ADAPTER_MAPPING_INVALID`).
77
+ `recorded` stays optional here; a sandbox session refuses a live adapter in its
78
+ own `spec-schema` gate (`SANDBOX_LIVE_ADAPTER_REFUSED`, see
79
+ [sandbox.md](sandbox.md)).
80
+
56
81
  ### 2. Scaffold
57
82
 
58
83
  ```bash
@@ -82,6 +107,15 @@ lifecycle field is set by the service), and the columns of the first `list`
82
107
  screen become the table view. Further entities are the implementing agent's
83
108
  work.
84
109
 
110
+ The optional sections scaffold declarations and fixtures, nothing that runs:
111
+ `src/research.ts` and `research-fixtures.json` (one example query and page in
112
+ the shape the recorded research adapter reads) for `research`; per adapter one
113
+ `src/adapters/<name>.ts` (the id without the module id, dots turned into
114
+ hyphens) and one recorded fixture stub at the declared `recorded` path, else
115
+ `adapters/<name>.recorded.json`; and one `templates/<name>.md` per template
116
+ body, with `templates` added to the package `files`. Implementing them follows the `module-new` and `integration-adapter`
117
+ skills.
118
+
85
119
  Files are written through the workspace Prettier, so the format gate passes
86
120
  without a rewrite. A directory that already holds `spec/module.yaml` or
87
121
  `translations/**` is extended, not rejected, and a failed run leaves nothing
@@ -119,7 +153,10 @@ pnpm --filter @flowdular/module-<dir> test
119
153
  `module validate` checks more than the schema: `platform.server` requires
120
154
  `src/platform.ts` and a `./platform` export, `platform.client` requires
121
155
  `src/client/index.ts` and a `./client` export, and every declared locale needs a
122
- `translations/<locale>.json` with the same key set as the others (an error).
156
+ `translations/<locale>.json` with the same key set as the others (an error). A
157
+ plural family such as `count.one`/`count.other` in `en` and
158
+ `count.one`/`count.few`/`count.many`/`count.other` in `pl` counts as one key,
159
+ and each locale must carry the categories its plural rules select.
123
160
  `module.json` version drift against `specVersion`, or a locale missing from
124
161
  `flowdular.json`, is reported as a warning.
125
162
 
@@ -356,6 +356,8 @@ variable falls back to the default and logs one warning about it.
356
356
  ## Production checklist
357
357
 
358
358
  - `pnpm verify` and `pnpm build` pass on the commit being shipped.
359
+ The container image only builds; it does not run the tests again, so a tag
360
+ must point at a commit whose CI verify passed.
359
361
  - `FD_ENV=production`, `FD_DATABASE_ADAPTER=postgresql`, separate
360
362
  `FD_DATABASE_URL` and `FD_DATABASE_MIGRATOR_URL` roles, neither `SUPERUSER`
361
363
  nor `BYPASSRLS` on the runtime role.
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-flowdular",
3
- "version": "0.3.2",
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.2"
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.2",
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.2"
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),
@@ -11,6 +11,7 @@ import {
11
11
  } from '@flowdular/sdk/server';
12
12
  import { createServerComposition as system_core } from '@flowdular/sdk/modules/system/platform';
13
13
  import { createServerComposition as access_core } from '@flowdular/sdk/modules/access/platform';
14
+ import { createServerComposition as adapters_core } from '@flowdular/sdk/modules/adapters/platform';
14
15
  import { createServerComposition as reports_core } from '@flowdular/sdk/modules/reports/platform';
15
16
  import { createServerComposition as metering_core } from '@flowdular/sdk/modules/metering/platform';
16
17
  import { createServerComposition as agents_core } from '@flowdular/sdk/modules/agents/platform';
@@ -26,6 +27,7 @@ import { createServerComposition as exports_core } from '@flowdular/sdk/modules/
26
27
  import { createServerComposition as import_core } from '@flowdular/sdk/modules/import/platform';
27
28
  import { createServerComposition as notifications_core } from '@flowdular/sdk/modules/notifications/platform';
28
29
  import { createServerComposition as profile_core } from '@flowdular/sdk/modules/profile/platform';
30
+ import { createServerComposition as research_core } from '@flowdular/sdk/modules/research/platform';
29
31
  import { createServerComposition as sandbox_core } from '@flowdular/sdk/modules/sandbox/platform';
30
32
  import { createServerComposition as search_core } from '@flowdular/sdk/modules/search/platform';
31
33
  import { createServerComposition as users_core } from '@flowdular/sdk/modules/users/platform';
@@ -60,6 +62,26 @@ export function composeModuleServer(
60
62
  }),
61
63
  moduleId: 'access.core',
62
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
+ },
63
85
  {
64
86
  ...reports_core({
65
87
  ...context,
@@ -177,7 +199,12 @@ export function composeModuleServer(
177
199
  agentDefinitions: context.agentDefinitions.forModule('connectors.core'),
178
200
  dataClasses: context.dataClasses.forModule('connectors.core'),
179
201
  capabilities: context.capabilities.forModule('connectors.core', {
180
- 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
+ ],
181
208
  requires: [],
182
209
  }),
183
210
  metrics: createModuleMetrics('connectors.core'),
@@ -203,8 +230,12 @@ export function composeModuleServer(
203
230
  agentDefinitions: context.agentDefinitions.forModule('documents.core'),
204
231
  dataClasses: context.dataClasses.forModule('documents.core'),
205
232
  capabilities: context.capabilities.forModule('documents.core', {
206
- provides: ['documents.attachments.v1'],
207
- requires: [],
233
+ provides: [
234
+ 'documents.attachments.v1',
235
+ 'documents.text.v1',
236
+ 'documents.templates.v1',
237
+ ],
238
+ requires: [{ id: 'connectors.egress.v1', optional: true }],
208
239
  }),
209
240
  metrics: createModuleMetrics('documents.core'),
210
241
  }),
@@ -242,7 +273,7 @@ export function composeModuleServer(
242
273
  agentDefinitions: context.agentDefinitions.forModule('import.core'),
243
274
  dataClasses: context.dataClasses.forModule('import.core'),
244
275
  capabilities: context.capabilities.forModule('import.core', {
245
- provides: ['import.ports.v1'],
276
+ provides: ['import.ports.v1', 'import.write.v1'],
246
277
  requires: [{ id: 'documents.attachments.v1' }],
247
278
  }),
248
279
  metrics: createModuleMetrics('import.core'),
@@ -276,6 +307,32 @@ export function composeModuleServer(
276
307
  }),
277
308
  moduleId: 'profile.core',
278
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
+ },
279
336
  {
280
337
  ...sandbox_core({
281
338
  ...context,