Tasks service. For slow work, see
Deferred answers.
Commands
defineCommand(name, options, handler) adds a slash command. The options are description,
options, scopes, blocks, env, config and progress, as on the
Promise API. options is a Schema.Struct, and its fields appear in the
composer in the order you write them. Give every field a description annotation, which the member
sees in the composer. The handler gets the decoded values:
src/taskCommand.tsx
.addCommand(TaskCommand). The answer can be public, private or a notice on its own;
see Answers. Reintersect waits up to 30 seconds for it.
A union of Schema.Literals is a choice; give each literal a title annotation for its label, or
use choice({ value: "Label" }, { description }). member({ description }), circle({ description })
and appObject(kind, { description }) from @reintersect/sdk/effect/app add pickers whose values
are { kind: "member", memberId, name } and so on. To receive only the ID, annotate a string
instead: Schema.String.annotations({ description: "Owner", jsonSchema: memberOption }).
Put the description on the field’s own schema. JSONSchema.make drops a description set on a
property signature that has a default, such as Schema.optionalWith(Schema.String, { default }), so
annotate the inner schema instead. The value table and the rules for unsupported shapes are in
Option schemas.
Message actions
defineMessageAction(id, options, handler) adds an item to the menu on a message. The options are
label, icon, context, scopes, blocks, env and config, as on the
Promise API:
src/saveAsTask.tsx
.addMessageAction(SaveAsTask). With context: "thread" or context: "conversation",
the handler also gets the thread or conversation in context; see
Context. The client in a message action always holds
conversations:read.
Progress updates
A command or message action can return aStream of answers instead of one answer. The first
element is the answer, and every later element replaces it in place, so a block can move through the
stages of long work:
src/deployCommand.tsx
visibility as const inside Stream.map, or the answer won’t
type-check. A failure or defect in the stream finishes the answer with the failure notice, and a later
element that changes the visibility or the block dies with ReintersectAppProgressMismatchError.
Delivery, coalescing and limits work as described in Progress updates.
In tests, host.settle runs the stream to the end on the TestClock, moving the clock through the
SDK’s waits, and host.answersOf(interactionToken) lists every answer it sent:
Events
defineEvent(name, handler) or defineEvent(name, options, handler) handles a lifecycle event. The
payload’s fields arrive at the top of the context, typed for the event:
src/onDecision.tsx
.addEvent(OnDecision). An event handler returns nothing. If it fails, the delivery
fails and Reintersect retries it with backoff, so make the handler safe to run twice. See
Delivery and the event catalog.
Routes
defineRoute({ method, path, scopes, blocks, env, handler }) serves your own HTTP endpoint, such as a
webhook from another service. The handler returns a Response. Routes belong to the app, not to an
install, so the context has reintersect.forInstall(installId) in place of a client:
src/ciWebhook.ts
params is typed from the path. Calling a client method whose scope the route doesn’t declare is a
type error, as it is for every piece. A failed route answers 500 with no details. See
HTTP routes for where routes are served.
MCP servers
.addMcp(handler, { auth }) works the same on an Effect app. ReintersectApp.layer mounts the
handler at /mcp next to the delivery path. See MCP servers.