Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/HELPERS-RUNTIME.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,12 @@ assigned invented writeback paths.
## Upstream gaps in slice S

The supplied provider list contains 48 names including the five original
providers. The pinned catalog also includes GitLab and Ramp, so regeneration
currently emits 50 namespaces. Forty have runtime clients. These ten are
providers. The pinned catalog also includes Bitbucket, GitLab and Ramp, so regeneration
currently emits 51 namespaces. Forty have runtime clients. These eleven are
explicitly unavailable, including in mock mode:

- airtable
- bitbucket
- docker-hub
- fathom
- gcp
Expand Down
2 changes: 1 addition & 1 deletion docs/SURFACE.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ No process runs between events: the handler wakes, executes to its next await, p
`verification`; an author callback remains TypeScript runtime code. The
distinction is explicit in the gate contract below.
3. **Helpers, not primitives.** Authors never see mount paths, tokens, or protocol frames. Named helpers wrap every substrate:
- `f.slack` / `f.github` / `f.linear` / … — **generated from the relayfile adapters** (50 providers → 50 namespaces for free), each verb compiling to a mount write. The receipt a helper returns *is* the journaled effect record (RFC Appendix A), so exactly-once dedup rides along invisibly.
- `f.slack` / `f.github` / `f.linear` / … — **generated from the relayfile adapters** (51 providers → 51 namespaces for free), each verb compiling to a mount write. The receipt a helper returns *is* the journaled effect record (RFC Appendix A), so exactly-once dedup rides along invisibly.
- `f.memory` — relayhistory: `f.memory.recall(query)`, `f.memory.why(task)`, `f.memory.learn(finding)`.
- auth — relayauth: never called directly; `workspace:` / `tools:` declarations compile to path-scoped tokens ("the filesystem paths *are* the permissions").
- `f.mcp` — one line to declare (`tools: { mcp: [stripe] }`), one call to use (`f.mcp.stripe.create_invoice({...})`). Preflight connects to every declared server before the run starts.
Expand Down
42 changes: 21 additions & 21 deletions packages/sdk/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
"license": "Apache-2.0",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.30.0",
"@relayfile/adapter-core": "0.6.2",
"@relayfile/relay-helpers": "0.4.12",
"@relayfile/adapter-core": "0.8.5",
"@relayfile/relay-helpers": "0.6.1",
Comment thread
khaliqgant marked this conversation as resolved.
Resolved
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
"@relayflows/surface": "2.0.59",
"@types/js-yaml": "^4.0.9",
"acorn": "^8.18.0",
Expand All @@ -75,8 +75,8 @@
"directory": "packages/sdk"
},
"peerDependencies": {
"@agent-relay/harness-driver": ">=12.3.1 <13",
"@agent-relay/sdk": ">=12.3.1 <13"
"@agent-relay/harness-driver": ">=12.3.1 <13 || >=15.1.0 <16",
"@agent-relay/sdk": ">=12.3.1 <13 || >=15.1.0 <16"
},
"peerDependenciesMeta": {
"@agent-relay/harness-driver": {
Expand All @@ -85,5 +85,10 @@
"@agent-relay/sdk": {
"optional": true
}
},
"overrides": {
"@relayflows/surface": {
"@relayfile/relay-helpers": "$@relayfile/relay-helpers"
}
}
}
2 changes: 1 addition & 1 deletion packages/sdk/src/hosted-extension-sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ const ADDRESS_SPACE_BYTES = 16 * 1024 * 1024 * 1024;
const DATA_BYTES = 3 * 1024 * 1024 * 1024;
const SURFACE_RUNTIME_SHA256 = OBJECT_FREEZE({
'flow.js': '2ba972849bf435bba36b918c348efeab71ec34d180cec7a722e535f482687a6a',
'helpers/providers.js': '4eb06d0d85ca0a3434bb2dbba7407e3d95eef2dfbedaeb0e2de0c0c0ea812457',
'helpers/providers.js': 'cba2c6767673b47047c6cb9549c63e797b5a9b1af53e293c50ea028f64d21025',
'provider-trigger.js': 'e2664c65397f93fb486eb6f1e756c7cec3f88b3851d79c23567cad986f80f1ff',
'schedule.js': '8fe72f176a75ec0b5f26e12db7a597575c259a2e2cbb59690f9dc20a5e63940b',
'triggers.js': '421609a6ea6d2ea77051dd4826a32e773b7db81dedd1dde91f4187b382cd46e3',
Expand Down
24 changes: 15 additions & 9 deletions packages/sdk/tests/authored-parallel-run-live.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -627,16 +627,23 @@ export default flow('serial-${name}', async f => {${body}
expect([...readFileSync(`${fixture.root}/effects`, 'utf8')].sort()).toEqual([...before, 'Z'].sort());
}, 120_000);

// Exercise completed-effect reuse with stable serial ordinals. Per-branch
// continuations can arrive in another order on replay and are correctly
// refused; serial-replay-identity.test.ts covers that separate contract.
it.each([
{ name: 'llm', localAgent: true, calls: 2, expected: ['L', 'R'], child: undefined, body: `
const output = { type: 'object', properties: { message: { type: 'string' } }, required: ['message'] };
const left = async () => { await f.llm('left', { output, model: 'test-model' }); await f.run('printf L >> effects'); };
const right = async () => { await f.llm('right', { output, model: 'test-model' }); await f.run('printf R >> effects'); };
await Promise.all([left(), right()]);` },
await Promise.all([
f.llm('left', { output, model: 'test-model' }),
f.llm('right', { output, model: 'test-model' }),
]);
await Promise.all([f.run('printf L >> effects'), f.run('printf R >> effects')]);` },
{ name: 'agent', localAgent: true, calls: 2, expected: ['L', 'R'], child: undefined, body: `
const left = async () => { await f.agent('left', { task: 'left', model: 'test-model' }); await f.run('printf L >> effects'); };
const right = async () => { await f.agent('right', { task: 'right', model: 'test-model' }); await f.run('printf R >> effects'); };
await Promise.all([left(), right()]);` },
await Promise.all([
f.agent('left', { task: 'left', model: 'test-model' }),
f.agent('right', { task: 'right', model: 'test-model' }),
]);
await Promise.all([f.run('printf L >> effects'), f.run('printf R >> effects')]);` },
{ name: 'dispatch', localAgent: false, calls: 0, expected: ['L', 'R', 'x', 'y'],
child: `import { flow } from '@relayflows/surface';
export default flow('child', async (f, input: { token: string }) => {
Expand All @@ -645,9 +652,8 @@ export default flow('child', async (f, input: { token: string }) => {
f.done('success');
});
`, body: `
const left = async () => { await f.dispatch('child', { token: 'x' }); await f.run('printf L >> effects'); };
const right = async () => { await f.dispatch('child', { token: 'y' }); await f.run('printf R >> effects'); };
await Promise.all([left(), right()]);` },
await Promise.all([f.dispatch('child', { token: 'x' }), f.dispatch('child', { token: 'y' })]);
await Promise.all([f.run('printf L >> effects'), f.run('printf R >> effects')]);` },
])('resumes $name branches without replaying completed effects', async ({ name, localAgent, calls, expected, child, body }) => {
const fixture = chainFixture();
closes.push(() => fixture.close());
Expand Down
3 changes: 3 additions & 0 deletions packages/sdk/tests/authored-root.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ describe('durable authored root', () => {
// This case checks authority and completion, not lease renewal. A 30 ms
// fake lease can expire when the full CI suite pauses the event loop.
journal.dispatchLeaseMs = 30_000;
// Ownership is renewed before execution; keep that renewal at the same
// duration instead of replacing the dispatch lease with the 30 ms default.
journal.peer.stepHeartbeat = async () => ({ lease_deadline_ms: Date.now() + 30_000 });

const result = await executeDurableAuthoredFlow(
loaded, journal as unknown as JournalClient, { topic: 'relay' },
Expand Down
18 changes: 18 additions & 0 deletions packages/sdk/tests/gitlab-helper-path.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { PreviewTransport, type gitlabClient } from '@relayfile/relay-helpers';
import { helperClients } from '@relayflows/surface/runtime';
import { expect, it } from 'vitest';

it('reads and writes the mounted GitLab project through the shipped helper factory', async () => {
const projectPath = 'acme/platform/web';
const path = '/gitlab/projects/acme/platform/web/issues/5__existing/meta.json';
const issue = { iid: 5, title: 'existing' };
const transport = new PreviewTransport({ fixtures: { [path]: issue } });
const client = helperClients.gitlab!({ transport }) as ReturnType<typeof gitlabClient>;

expect(await client.issues.read({ projectPath, issueIid: 5, slug: 'existing' })).toEqual(issue);
await client.issues.write({ projectPath }, { title: 'new issue' });
expect(transport.actions).toMatchObject([
{ kind: 'provider.read', path },
{ kind: 'provider.write', path: expect.stringMatching(/^\/gitlab\/projects\/acme\/platform\/web\/issues\/[^/]+\.json$/u), body: { title: 'new issue' } },
]);
});
1 change: 1 addition & 0 deletions packages/sdk/tsconfig.tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"tests/run-from-digest.test.ts",
"tests/run-digest-live.test.ts",
"tests/helpers-fanout.test.ts",
"tests/gitlab-helper-path.test.ts",
"tests/authored-helpers.test.ts",
"tests/fixtures/build.flow.ts",
"tests/typed-output.test.ts",
Expand Down
21 changes: 20 additions & 1 deletion packages/surface/AUTHORING.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,13 +465,14 @@ The kernel verification fields are `exit_code`, `output_contains` and `json_sche

## Helper namespaces

`Ctx extends Helpers` exposes 50 namespaces. `f.mcp` is an additional context member. `f.slack` is included in Helpers; see [Slack semantics](https://github.com/AgentWorkforce/flows/blob/main/docs/SLACK-HELPER.md). Resource counts describe the pinned adapter catalog, not bespoke helper methods or an assurance of credentials.
`Ctx extends Helpers` exposes 51 namespaces. `f.mcp` is an additional context member. `f.slack` is included in Helpers; see [Slack semantics](https://github.com/AgentWorkforce/flows/blob/main/docs/SLACK-HELPER.md). Resource counts describe the pinned adapter catalog, not bespoke helper methods or an assurance of credentials.

| Namespace | Type | Pinned adapter resources |
| --- | --- | --- |
| `f.airtable` | `AirtableHelper` | None in pinned catalog |
| `f.asana` | `AsanaHelper` | 3 |
| `f.azureBlob` | `AzureBlobHelper` | 2 |
| `f.bitbucket` | `BitbucketHelper` | None in pinned catalog |
| `f.box` | `BoxHelper` | 2 |
| `f.calendly` | `CalendlyHelper` | 3 |
| `f.clickup` | `ClickupHelper` | 4 |
Expand Down Expand Up @@ -573,6 +574,17 @@ Exported from the package root (`import { github } from '@relayflows/surface'`).
| `azure_blob.file_created(filter?: WebhookFilter)` | `azure-blob` | `file.created` |
| `azure_blob.file_deleted(filter?: WebhookFilter)` | `azure-blob` | `file.deleted` |
| `azure_blob.file_updated(filter?: WebhookFilter)` | `azure-blob` | `file.updated` |
| `bitbucket.pullrequest_comment_created(filter?: WebhookFilter)` | `bitbucket` | `pullrequest:comment_created` |
| `bitbucket.pullrequest_comment_deleted(filter?: WebhookFilter)` | `bitbucket` | `pullrequest:comment_deleted` |
| `bitbucket.pullrequest_created(filter?: WebhookFilter)` | `bitbucket` | `pullrequest:created` |
| `bitbucket.pullrequest_fulfilled(filter?: WebhookFilter)` | `bitbucket` | `pullrequest:fulfilled` |
| `bitbucket.pullrequest_rejected(filter?: WebhookFilter)` | `bitbucket` | `pullrequest:rejected` |
| `bitbucket.pullrequest_updated(filter?: WebhookFilter)` | `bitbucket` | `pullrequest:updated` |
| `bitbucket.repo_commit_status_created(filter?: WebhookFilter)` | `bitbucket` | `repo:commit_status_created` |
| `bitbucket.repo_commit_status_updated(filter?: WebhookFilter)` | `bitbucket` | `repo:commit_status_updated` |
| `bitbucket.repo_deleted(filter?: WebhookFilter)` | `bitbucket` | `repo:deleted` |
| `bitbucket.repo_push(filter?: WebhookFilter)` | `bitbucket` | `repo:push` |
| `bitbucket.repo_updated(filter?: WebhookFilter)` | `bitbucket` | `repo:updated` |
| `box.file_created(filter?: WebhookFilter)` | `box` | `file.created` |
| `box.file_deleted(filter?: WebhookFilter)` | `box` | `file.deleted` |
| `box.file_updated(filter?: WebhookFilter)` | `box` | `file.updated` |
Expand Down Expand Up @@ -1203,6 +1215,7 @@ export const providerEventTypes = Object.freeze({
"airtable": Object.freeze(["base.create","base.delete","base.update","record.create","record.delete","record.update","table.create","table.delete","table.update"] as const),
"asana": Object.freeze(["project.added","project.changed","project.deleted","project.removed","section.added","section.changed","section.deleted","section.removed","task.added","task.changed","task.completed","task.deleted","task.removed","workspace.added","workspace.changed","workspace.deleted","workspace.removed"] as const),
"azure-blob": Object.freeze(["file.created","file.deleted","file.updated"] as const),
"bitbucket": Object.freeze(["pullrequest:comment_created","pullrequest:comment_deleted","pullrequest:created","pullrequest:fulfilled","pullrequest:rejected","pullrequest:updated","repo:commit_status_created","repo:commit_status_updated","repo:deleted","repo:push","repo:updated"] as const),
"box": Object.freeze(["file.created","file.deleted","file.updated"] as const),
"calendly": Object.freeze(["event_type.canceled","event_type.created","event_type.deleted","event_type.updated","invitee.canceled","invitee.created","invitee.deleted","invitee.updated","scheduled_event.canceled","scheduled_event.created","scheduled_event.deleted","scheduled_event.updated"] as const),
"clickup": Object.freeze(["folder.created","folder.deleted","folder.updated","list.created","list.deleted","list.updated","space.created","space.deleted","space.updated","task.created","task.deleted","task.updated"] as const),
Expand Down Expand Up @@ -1445,6 +1458,12 @@ export type AsanaHelper = JournalHelper<ReturnType<typeof asanaClient>>;
export type AzureBlobHelper = JournalHelper<ReturnType<typeof azureBlobClient>>;
```

### BitbucketHelper (not exported from the package root)

```ts
export type BitbucketHelper = UnavailableHelper;
```

### BoxHelper (not exported from the package root)

```ts
Expand Down
Loading
Loading