loopengine 0.3.1 → 0.3.3

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 (37) hide show
  1. package/dist/core/agent-config.d.ts +2 -0
  2. package/dist/core/http-notifier.js +14 -3
  3. package/dist/core/http-notify-triggers/telegram.d.ts +20 -1
  4. package/dist/core/http-notify-triggers/telegram.js +63 -17
  5. package/dist/core/messaging/connector.d.ts +7 -0
  6. package/dist/core/messaging/connector.js +1 -1
  7. package/dist/core/messaging/connectors/slack/events.d.ts +2 -1
  8. package/dist/core/messaging/connectors/slack/events.js +6 -2
  9. package/dist/core/messaging/connectors/slack/setup.d.ts +2 -1
  10. package/dist/core/messaging/connectors/slack/setup.js +10 -5
  11. package/dist/core/messaging/connectors/telegram/format.d.ts +6 -0
  12. package/dist/core/messaging/connectors/telegram/format.js +57 -0
  13. package/dist/core/messaging/connectors/telegram/index.d.ts +39 -0
  14. package/dist/core/messaging/connectors/telegram/index.js +136 -0
  15. package/dist/core/messaging/connectors/telegram/store.d.ts +31 -0
  16. package/dist/core/messaging/connectors/telegram/store.js +69 -0
  17. package/dist/core/messaging/connectors/telegram/updates.d.ts +40 -0
  18. package/dist/core/messaging/connectors/telegram/updates.js +112 -0
  19. package/dist/core/messaging/jobs.d.ts +44 -0
  20. package/dist/core/messaging/jobs.js +100 -0
  21. package/dist/core/messaging/threads.d.ts +12 -0
  22. package/dist/core/messaging/threads.js +36 -0
  23. package/dist/core/notifier-file.d.ts +13 -5
  24. package/dist/core/notifier-file.js +41 -6
  25. package/dist/core/system-agents/workflow-planner.d.ts +37 -4
  26. package/dist/core/system-agents/workflow-planner.js +79 -5
  27. package/dist/core/workflow-triggers.d.ts +15 -5
  28. package/dist/core/workflow-triggers.js +25 -8
  29. package/dist/index.d.ts +6 -3
  30. package/dist/index.js +6 -3
  31. package/dist/web/agents-config-page.js +168 -50
  32. package/dist/web/notifier-admin.d.ts +69 -13
  33. package/dist/web/notifier-admin.js +127 -31
  34. package/dist/web/workflow-admin.d.ts +10 -0
  35. package/dist/web/workflow-admin.js +42 -3
  36. package/dist/web/workflow-page.js +93 -2
  37. package/package.json +1 -1
@@ -16,8 +16,19 @@ How to plan:
16
16
  - nothing found can do it: set "missing" to what's needed, and add it to gaps.
17
17
  3. Rules: every tool the workflow uses gets one. Reading and searching: allow. Anything a person should see first (sending, publishing, paying, creating something others see): ask. Never set deny unless asked.
18
18
  4. Skill: the steps in order, as numbered instructions the agent follows, naming the tools and saying exactly when to stop and ask. Write it so the agent keeps going after each answer or decision.
19
- 5. Trigger: a schedule ("every Monday at 9" is every "7d", at "09:00", weekday 1) or on_request (started from Chat or a message). startMessage is what the agent receives when it starts.
20
- 6. Notify (scheduled workflows): whether each run's result is sent to the person through the agent's messaging app. true when the result is for them and no step already delivers it ("tell me", "send me", "let me know", a summary or findings that no step posts or emails). false when a step already sends it (posting to a channel, emailing a report): it would arrive twice. false when the work itself is the outcome (answering emails, filing tasks), where a message every run is noise; questions and approvals reach them either way. Say why in one short sentence.
19
+ 5. Trigger: how a run starts. startMessage is what the agent receives when it starts.
20
+ - schedule: a time or interval ("every Monday at 9" is every "7d", at "09:00", weekday 1).
21
+ - webhook: when something happens in another service that can call a URL ("when a Stripe dispute comes in", "when a GitHub issue is opened", "when a Shopify order is paid", "when our app sends a new signup"). Fill in "webhook" (below). Prefer it over polling on a schedule whenever the service sends webhooks.
22
+ - on_request: started by a person, from Chat, a message, the Run button or an app's API call.
23
+ A workflow that runs on a schedule and on an event as well: trigger schedule, and fill in "webhook" too.
24
+ webhook: sender ("stripe", "github", "shopify", "standard" for Standard Webhooks senders such as Clerk, Resend or Svix, else "custom" for the person's own app or any other service); a short name ("Stripe: new disputes"); setup: one sentence on what to pick in the sender's webhook settings; filter: which events start a run; mapping: each parameter from a dot path in the payload. What senders send:
25
+ - Stripe: one endpoint can get many event types, so filter path "type", values the event names (charge.dispute.created, checkout.session.completed, invoice.payment_failed, customer.subscription.deleted). The object is at data.object: data.object.id, data.object.customer, data.object.amount, data.object.charge.
26
+ - GitHub: the event type is chosen in GitHub's settings (Issues, Pull requests, Pushes), so say it in setup; filter path "action", values such as opened, closed, created. Fields: issue.number, issue.title, pull_request.number, repository.full_name, sender.login.
27
+ - Shopify: one webhook per topic (orders/paid, orders/create, customers/create), chosen in Shopify, so say it in setup and use no filter. The body is the object itself: id, name (the order number, like #1001), email, total_price, financial_status.
28
+ - standard: filter path "type" (user.created, email.bounced); fields under data (data.id, data.email_addresses).
29
+ - custom: whatever the person's app sends; say in setup what to post (JSON with these fields) and use the paths you described. Set "header" when the person names the signature header.
30
+ Every required parameter should come from the payload through mapping; parameters are the only way event details reach the run.
31
+ 6. Notify (scheduled and webhook workflows): whether each run's result is sent to the person through the agent's messaging app. true when the result is for them and no step already delivers it ("tell me", "send me", "let me know", a summary or findings that no step posts or emails). false when a step already sends it (posting to a channel, emailing a report): it would arrive twice. false when the work itself is the outcome (answering emails, filing tasks), where a message every run is noise; questions and approvals reach them either way. Say why in one short sentence.
21
32
  7. Parameters: if the job is about something that changes from run to run (an order, a ticket, an alert, a customer, a date range), declare it as a parameter: a short snake_case name, a one-line description, and required when a run makes no sense without it. Write $name wherever its value goes, in startMessage and in the skill ("Check the refund request for order $order_number."). Apps and people fill them in when they start a run. A scheduled job that finds its own work ("check the inbox every 10 minutes", "last week's sales") needs none. Don't make fixed settings, such as the repository or the channel, parameters: write them into the skill.
22
33
  8. Target: an existing agent only when it already has (or can install) the tools, none of the workflow's rules would change a decision it already has, and the same people would approve. Otherwise a new agent, with a short lowercase-hyphenated name and a system prompt saying who it is and what it's for. Give the reason either way.
23
34
  9. keys: the setting names its abilities need that list_connections or the agents don't show as set (from ability_details settings). Not for MCP servers: their keys are asked for on the plan.
@@ -59,7 +70,7 @@ export function workflowPlannerTools(withMcp = false) {
59
70
  trigger: {
60
71
  type: 'object',
61
72
  properties: {
62
- kind: { type: 'string', enum: ['schedule', 'on_request'] },
73
+ kind: { type: 'string', enum: ['schedule', 'webhook', 'on_request'] },
63
74
  description: { type: 'string' },
64
75
  every: { type: 'string' },
65
76
  at: { type: 'string' },
@@ -74,9 +85,22 @@ export function workflowPlannerTools(withMcp = false) {
74
85
  needsApprovals: { type: 'boolean' },
75
86
  skill: { type: 'object', properties: { description: { type: 'string' }, body: { type: 'string' } }, required: ['description', 'body'] },
76
87
  startMessage: { type: 'string' },
88
+ webhook: {
89
+ type: 'object',
90
+ description: 'When an outside service\'s events start runs: its sender, which events, and the payload fields that fill each parameter.',
91
+ properties: {
92
+ sender: { type: 'string', enum: ['stripe', 'github', 'shopify', 'standard', 'custom'] },
93
+ name: { type: 'string' },
94
+ header: { type: 'string', description: 'custom only: the signature header, when known' },
95
+ setup: { type: 'string', description: 'one sentence: what to pick in the sender\'s webhook settings' },
96
+ filter: { type: 'object', properties: { path: { type: 'string' }, values: { type: 'array', items: { type: 'string' } } }, required: ['path', 'values'] },
97
+ mapping: { type: 'object', description: 'parameter name -> dot path in the payload', additionalProperties: { type: 'string' } },
98
+ },
99
+ required: ['sender', 'name'],
100
+ },
77
101
  notify: {
78
102
  type: 'object',
79
- description: 'Scheduled workflows: whether each run\'s result goes to the person through the agent\'s messaging app, and why in one short sentence.',
103
+ description: 'Scheduled and webhook workflows: whether each run\'s result goes to the person through the agent\'s messaging app, and why in one short sentence.',
80
104
  properties: { on: { type: 'boolean' }, reason: { type: 'string' } },
81
105
  required: ['on', 'reason'],
82
106
  },
@@ -144,7 +168,9 @@ export function validateWorkflowPlan(raw) {
144
168
  ...(typeof triggerRaw.at === 'string' && /^\d{1,2}:\d{2}$/.test(triggerRaw.at) ? { at: triggerRaw.at } : {}),
145
169
  ...(typeof triggerRaw.weekday === 'number' && triggerRaw.weekday >= 0 && triggerRaw.weekday <= 7 ? { weekday: triggerRaw.weekday } : {}),
146
170
  }
147
- : { kind: 'on_request', description: str(triggerRaw.description, 200) ?? 'when asked' };
171
+ : triggerRaw.kind === 'webhook'
172
+ ? { kind: 'webhook', description: str(triggerRaw.description, 200) ?? 'when an event comes in' }
173
+ : { kind: 'on_request', description: str(triggerRaw.description, 200) ?? 'when asked' };
148
174
  // An MCP step's permissions are set when its server becomes an ability.
149
175
  const mcpTools = new Set(steps.flatMap((s) => (s.mcp && s.tool ? [s.tool] : [])));
150
176
  const rules = (Array.isArray(r.rules) ? r.rules : []).flatMap((x) => {
@@ -172,6 +198,15 @@ export function validateWorkflowPlan(raw) {
172
198
  const startMessage = str(r.startMessage, 1000) ?? `Run the "${name}" workflow.`;
173
199
  const gaps = Array.isArray(r.gaps) ? r.gaps.flatMap((g) => (str(g, 400) ? [str(g, 400)] : [])).slice(0, 10) : [];
174
200
  const planned = planParameters(r.parameters, { startMessage, skillBody, scheduled: trigger.kind === 'schedule' });
201
+ const webhook = planWebhook(r.webhook, planned.parameters);
202
+ // A webhook described means events start it; a webhook start with none
203
+ // described is started by hand.
204
+ if (webhook && trigger.kind === 'on_request')
205
+ trigger.kind = 'webhook';
206
+ if (!webhook && trigger.kind === 'webhook')
207
+ trigger.kind = 'on_request';
208
+ if (webhook)
209
+ planned.notes.push(...webhookNotes(webhook, planned.parameters));
175
210
  return {
176
211
  name,
177
212
  id,
@@ -185,6 +220,7 @@ export function validateWorkflowPlan(raw) {
185
220
  skill: { description: str(skillRaw.description, 400) ?? summary, body: skillBody },
186
221
  startMessage,
187
222
  parameters: planned.parameters,
223
+ ...(webhook ? { webhook } : {}),
188
224
  notify: planNotify(r.notify),
189
225
  target: {
190
226
  kind: targetRaw.kind === 'existing' && str(targetRaw.agent, 80) ? 'existing' : 'new',
@@ -195,6 +231,44 @@ export function validateWorkflowPlan(raw) {
195
231
  gaps: [...gaps, ...planned.notes].slice(0, 12),
196
232
  };
197
233
  }
234
+ const SENDERS = ['stripe', 'github', 'shopify', 'standard', 'custom'];
235
+ /** A plan's webhook, tidied: a known sender, a filter with values, and a
236
+ * mapping only for parameters the workflow has. Undefined without a
237
+ * sender. Also used on a plan edited before Create. */
238
+ export function planWebhook(raw, parameters) {
239
+ if (!raw || typeof raw !== 'object')
240
+ return undefined;
241
+ const w = raw;
242
+ if (!SENDERS.includes(w.sender))
243
+ return undefined;
244
+ const sender = w.sender;
245
+ const filterRaw = (w.filter ?? {});
246
+ const filterPath = str(filterRaw.path, 120);
247
+ const values = Array.isArray(filterRaw.values) ? filterRaw.values.flatMap((v) => (str(v, 120) ? [str(v, 120)] : [])).slice(0, 10) : [];
248
+ const mapping = {};
249
+ if (w.mapping && typeof w.mapping === 'object' && !Array.isArray(w.mapping)) {
250
+ for (const [param, path] of Object.entries(w.mapping)) {
251
+ const p = str(path, 200);
252
+ if (p && parameters[param] && /^[A-Za-z0-9_.-]+$/.test(p))
253
+ mapping[param] = p;
254
+ }
255
+ }
256
+ const header = sender === 'custom' ? str(w.header, 80) : undefined;
257
+ return {
258
+ sender,
259
+ name: str(w.name, 80) ?? `${sender === 'custom' ? 'App' : sender[0].toUpperCase() + sender.slice(1)} events`,
260
+ ...(header && /^[A-Za-z0-9-]{1,80}$/.test(header) ? { header } : {}),
261
+ ...(filterPath && values.length ? { filter: { path: filterPath, values } } : {}),
262
+ ...(Object.keys(mapping).length ? { mapping } : {}),
263
+ ...(str(w.setup, 300) ? { setup: str(w.setup, 300) } : {}),
264
+ };
265
+ }
266
+ /** What the person should know about a planned webhook: required
267
+ * parameters nothing in the payload fills. */
268
+ export function webhookNotes(webhook, parameters) {
269
+ const unfilled = Object.entries(parameters).filter(([name, p]) => p.required && !webhook.mapping?.[name]).map(([name]) => name);
270
+ return unfilled.length ? [`Nothing in the webhook's payload fills ${unfilled.join(', ')}, so those runs would fail; map ${unfilled.length > 1 ? 'them' : 'it'} on the workflow's Webhooks after it's created.`] : [];
271
+ }
198
272
  /** A plan's notify choice; off when the planner gave none. */
199
273
  export function planNotify(raw) {
200
274
  const n = raw && typeof raw === 'object' ? raw : {};
@@ -57,9 +57,12 @@ export interface WorkflowTrigger {
57
57
  /** Delivery ids already handled (the latest 200), for retries. */
58
58
  seen_ids: string[];
59
59
  }
60
- /** A trigger as the console and API show it: no token hash, no secret. */
60
+ /** A trigger as the console and API show it: no token hash, no secret,
61
+ * only whether one is set. */
61
62
  export type PublicTrigger = Omit<WorkflowTrigger, 'token_hash' | 'verify' | 'seen_ids'> & {
62
- verify?: Omit<TriggerVerify, 'secret'>;
63
+ verify?: Omit<TriggerVerify, 'secret'> & {
64
+ secret_set: boolean;
65
+ };
63
66
  };
64
67
  export declare class TriggerError extends Error {
65
68
  }
@@ -96,8 +99,14 @@ export interface TriggerSettings {
96
99
  callback_url?: unknown;
97
100
  }
98
101
  /** Checks settings for a trigger of `workflow`; throws TriggerError saying
99
- * what's wrong. `existing` keeps a sender's secret when only the sender
100
- * is given again (the console never sees the secret). */
102
+ * what's wrong. With `existing` (a change), a setting left out keeps its
103
+ * value and null removes it, and a sender's secret is kept when only the
104
+ * sender is given again (the console never sees the secret).
105
+ *
106
+ * A trigger that checks a signature can be made turned off without its
107
+ * secret yet: the workflow planner makes one that way, for the person to
108
+ * add the secret the sender shows them. It can't be turned on until the
109
+ * secret is there. */
101
110
  export declare function checkTriggerSettings(input: TriggerSettings, workflow: Workflow, existing?: WorkflowTrigger): Pick<WorkflowTrigger, 'name' | 'enabled' | 'verify' | 'filter' | 'mapping' | 'payload_param' | 'notify' | 'callback_url'>;
102
111
  /** Makes a trigger for a workflow; returns it with its token (shown once). */
103
112
  export declare function createTrigger(agent: string, workflow: Workflow, input: TriggerSettings): Promise<{
@@ -113,7 +122,8 @@ export declare function rotateTrigger(id: string): Promise<{
113
122
  export declare function listTriggers(agent: string, workflowId?: string): Promise<WorkflowTrigger[]>;
114
123
  /** The trigger a URL's token belongs to. */
115
124
  export declare function findTriggerByToken(token: string): Promise<WorkflowTrigger | undefined>;
116
- /** Whether the sender's own signature on the raw body is right. */
125
+ /** Whether the sender's own signature on the raw body is right. Never
126
+ * with no secret: anyone can sign with an empty key. */
117
127
  export declare function verifySenderSignature(verify: TriggerVerify, headers: Record<string, string | string[] | undefined>, raw: Buffer, nowS?: number): boolean;
118
128
  /** The payload: JSON, a form, or plain text (as { body }). */
119
129
  export declare function parsePayload(raw: Buffer, contentType: string | undefined): unknown;
@@ -96,16 +96,30 @@ export function publicTrigger(t) {
96
96
  const { token_hash: _hash, verify, seen_ids: _seen, ...rest } = t;
97
97
  if (!verify)
98
98
  return rest;
99
- const { secret: _secret, ...shown } = verify;
100
- return { ...rest, verify: shown };
99
+ const { secret, ...shown } = verify;
100
+ return { ...rest, verify: { ...shown, secret_set: Boolean(secret) } };
101
101
  }
102
102
  /** Checks settings for a trigger of `workflow`; throws TriggerError saying
103
- * what's wrong. `existing` keeps a sender's secret when only the sender
104
- * is given again (the console never sees the secret). */
103
+ * what's wrong. With `existing` (a change), a setting left out keeps its
104
+ * value and null removes it, and a sender's secret is kept when only the
105
+ * sender is given again (the console never sees the secret).
106
+ *
107
+ * A trigger that checks a signature can be made turned off without its
108
+ * secret yet: the workflow planner makes one that way, for the person to
109
+ * add the secret the sender shows them. It can't be turned on until the
110
+ * secret is there. */
105
111
  export function checkTriggerSettings(input, workflow, existing) {
112
+ if (existing) {
113
+ const keys = ['verify', 'filter', 'mapping', 'payload_param', 'notify', 'callback_url'];
114
+ input = { ...input };
115
+ for (const key of keys)
116
+ if (input[key] === undefined && existing[key] !== undefined)
117
+ input[key] = existing[key];
118
+ }
106
119
  const name = typeof input.name === 'string' && input.name.trim() ? input.name.trim().slice(0, 80) : existing?.name ?? 'Webhook';
107
120
  if (input.enabled !== undefined && typeof input.enabled !== 'boolean')
108
121
  throw new TriggerError('enabled must be true or false');
122
+ const enabled = input.enabled === undefined ? existing?.enabled ?? true : input.enabled;
109
123
  let verify;
110
124
  if (input.verify !== undefined && input.verify !== null) {
111
125
  const v = input.verify;
@@ -113,10 +127,10 @@ export function checkTriggerSettings(input, workflow, existing) {
113
127
  throw new TriggerError(`verify.sender must be one of ${SENDERS.join(', ')}`);
114
128
  const kept = existing?.verify && existing.verify.sender === v.sender ? existing.verify.secret : '';
115
129
  const secret = typeof v.secret === 'string' && v.secret.trim() ? v.secret.trim() : kept;
116
- if (!secret)
130
+ if (!secret && enabled)
117
131
  throw new TriggerError(`Give the signing secret ${v.sender === 'custom' ? 'the sender' : v.sender} shows for this webhook, so its signature can be checked.`);
118
132
  verify = { sender: v.sender, secret };
119
- if (v.sender === 'standard' && !/^(whsec_)?[A-Za-z0-9+/]+=*$/.test(secret))
133
+ if (v.sender === 'standard' && secret && !/^(whsec_)?[A-Za-z0-9+/]+=*$/.test(secret))
120
134
  throw new TriggerError('A Standard Webhooks secret looks like "whsec_" followed by base64 text.');
121
135
  if (v.sender === 'custom') {
122
136
  const headerName = typeof v.header === 'string' ? v.header.trim() : '';
@@ -169,7 +183,7 @@ export function checkTriggerSettings(input, workflow, existing) {
169
183
  const callbackUrl = input.callback_url ? checkCallbackUrl(input.callback_url) : undefined;
170
184
  return {
171
185
  name,
172
- enabled: input.enabled === undefined ? existing?.enabled ?? true : input.enabled,
186
+ enabled,
173
187
  ...(verify ? { verify } : {}),
174
188
  ...(filter ? { filter } : {}),
175
189
  ...(mapping ? { mapping } : {}),
@@ -238,8 +252,11 @@ const header = (headers, name) => {
238
252
  const v = headers[name];
239
253
  return Array.isArray(v) ? v[0] : v;
240
254
  };
241
- /** Whether the sender's own signature on the raw body is right. */
255
+ /** Whether the sender's own signature on the raw body is right. Never
256
+ * with no secret: anyone can sign with an empty key. */
242
257
  export function verifySenderSignature(verify, headers, raw, nowS = Math.floor(Date.now() / 1000)) {
258
+ if (!verify.secret)
259
+ return false;
243
260
  if (verify.sender === 'stripe') {
244
261
  const parts = (header(headers, 'stripe-signature') ?? '').split(',').map((p) => p.split('='));
245
262
  const t = Number(parts.find(([k]) => k === 't')?.[1]);
package/dist/index.d.ts CHANGED
@@ -12,16 +12,19 @@ export { listDeliveries, recordDelivery, type NotifierDelivery } from '#core/not
12
12
  export { describeNotifier } from '#core/http-notifier.js';
13
13
  export { Scheduler, ScheduleError, FileSchedulerStore, getSchedulerStore, setSchedulerStore, createScheduledTask, listScheduledTasks, getScheduledTask, cancelScheduledTask, pauseScheduledTask, resumeScheduledTask, runScheduledTaskNow, setScheduledTaskNotify, deleteScheduledTask, nextScheduledWakeAt, moveScheduledTasksToTimeZone, importLegacyScheduledTasks, parseEvery, describeSchedule, type ScheduledTask, type ScheduledTaskRun, type ScheduledTaskStatus, type Schedule, type SchedulerStore, type FireTask, type CreateScheduledTaskInput } from '#core/scheduler.js';
14
14
  export { NOTIFIER_SLACK_SIGNING_SECRET_VAR } from '#core/notifier-file.js';
15
- export { firstSighting, handleInboundMessage, handleMessagingAction, settleThreadChoice, isPlainApproval, type MessagingDeps, type MessagingDecision, type DecisionOutcome } from '#core/messaging/engine.js';
15
+ export { firstSighting, handleInboundMessage, handleMessagingAction, settleThreadChoice, isPlainApproval, outcomeText, type MessagingDeps, type MessagingDecision, type DecisionOutcome } from '#core/messaging/engine.js';
16
+ export { handleJobMessage, isJobMessage, settleJobThread, jobSessionId, findJobFor, type JobDeps, type JobTurn } from '#core/messaging/jobs.js';
16
17
  export { formatArgs, formatChange, labelFor } from '#core/messaging/format-args.js';
17
18
  export { decideReply, proposeEdit, describeChange, type ReplyDecision, type DecisionRequest } from '#core/messaging/decide.js';
18
- export { recordThread, findThread, findThreadByPending, type ThreadLink } from '#core/messaging/threads.js';
19
+ export { recordThread, findThread, findThreadByPending, recordJobThread, findJobThread, findJobThreadBySession, type ThreadLink, type JobThread } from '#core/messaging/threads.js';
19
20
  export type { InboundMessage, MessagingConnector, ThreadMessage, MessagingChoice, MessagingAction } from '#core/messaging/connector.js';
20
21
  export { parseSlackEvent, createSlackConnector, verifySlackRequest, parseSlackInteraction, openSlackAnswerModal, replyInSlack, replyPrivatelyInSlack, slackOutcomeText, listSlackChannels, listSlackChannelMembers, slackAppManifest, toSlackMrkdwn, slackArgs, slackEscape, type SlackEvent, type SlackInteraction, type SlackChannel } from '#core/messaging/connectors/slack/index.js';
21
22
  export { createDecisionToken, verifyDecisionToken, decidePath, decideLinkMaker, decisionLinkTtlMs, type DecisionKind } from '#core/approval-links.js';
22
23
  export { renderDecidePage, renderDecideGonePage, renderDecideInvalidPage, type DecidePageItem } from './web/decide-page.js';
23
- export { sendTestWebhook } from '#core/http-notify-triggers/webhook.js';
24
+ export { sendTestWebhook, publicBaseUrl } from '#core/http-notify-triggers/webhook.js';
24
25
  export { describeAgentNotifier, saveAgentNotifier, removeAgentNotifier, setAgentPrimaryNotifier, testAgentNotifier, listAgentPending, slackCredentialsFor, slackConversationsOn, slackClickAllowed, agentApprovers, setAgentApprovers, listAgentSlackMembers, listAgentSlackChannels, announceToNotifier, type DefaultWebhook } from './web/notifier-admin.js';
26
+ export { clickAllowed, conversationsOn, jobsOn, jobAllowed, connectionPublicUrl, telegramCredentialsFor, telegramHomeChat, listAgentTelegramPeople, setupAgentTelegramWebhook, agentTelegramWebhookInfo, type TwoWayApp } from './web/notifier-admin.js';
27
+ export { createTelegramConnector, parseTelegramUpdate, verifyTelegramRequest, telegramCallbackData, answerTelegramClick, editTelegramMessage, telegramBotUsername, setTelegramWebhook, telegramWebhookInfo, callTelegram, recordTelegramMessage, readTelegramThread, telegramRootOf, telegramPeople, toTelegramHtml, telegramEscape, telegramArgs, type TelegramUpdate, type TelegramButton, type TelegramPerson, } from '#core/messaging/connectors/telegram/index.js';
25
28
  export type * from './core/loop-events.js';
26
29
  export { streamMessage, streamMessageWithCallbacks, sendMessage, approveCall, denyCall, answerQuestion, getSessionHistory, } from './core/client.js';
27
30
  export type { RequestOptions, SendMessageResult, PendingApprovalResult, PendingQuestionResult, PendingResult, MessageResult, StreamMessageCallbacks, } from './core/client.js';
package/dist/index.js CHANGED
@@ -14,15 +14,18 @@ export { listDeliveries, recordDelivery } from '#core/notifier-deliveries.js';
14
14
  export { describeNotifier } from '#core/http-notifier.js';
15
15
  export { Scheduler, ScheduleError, FileSchedulerStore, getSchedulerStore, setSchedulerStore, createScheduledTask, listScheduledTasks, getScheduledTask, cancelScheduledTask, pauseScheduledTask, resumeScheduledTask, runScheduledTaskNow, setScheduledTaskNotify, deleteScheduledTask, nextScheduledWakeAt, moveScheduledTasksToTimeZone, importLegacyScheduledTasks, parseEvery, describeSchedule } from '#core/scheduler.js';
16
16
  export { NOTIFIER_SLACK_SIGNING_SECRET_VAR } from '#core/notifier-file.js';
17
- export { firstSighting, handleInboundMessage, handleMessagingAction, settleThreadChoice, isPlainApproval } from '#core/messaging/engine.js';
17
+ export { firstSighting, handleInboundMessage, handleMessagingAction, settleThreadChoice, isPlainApproval, outcomeText } from '#core/messaging/engine.js';
18
+ export { handleJobMessage, isJobMessage, settleJobThread, jobSessionId, findJobFor } from '#core/messaging/jobs.js';
18
19
  export { formatArgs, formatChange, labelFor } from '#core/messaging/format-args.js';
19
20
  export { decideReply, proposeEdit, describeChange } from '#core/messaging/decide.js';
20
- export { recordThread, findThread, findThreadByPending } from '#core/messaging/threads.js';
21
+ export { recordThread, findThread, findThreadByPending, recordJobThread, findJobThread, findJobThreadBySession } from '#core/messaging/threads.js';
21
22
  export { parseSlackEvent, createSlackConnector, verifySlackRequest, parseSlackInteraction, openSlackAnswerModal, replyInSlack, replyPrivatelyInSlack, slackOutcomeText, listSlackChannels, listSlackChannelMembers, slackAppManifest, toSlackMrkdwn, slackArgs, slackEscape } from '#core/messaging/connectors/slack/index.js';
22
23
  export { createDecisionToken, verifyDecisionToken, decidePath, decideLinkMaker, decisionLinkTtlMs } from '#core/approval-links.js';
23
24
  export { renderDecidePage, renderDecideGonePage, renderDecideInvalidPage } from './web/decide-page.js';
24
- export { sendTestWebhook } from '#core/http-notify-triggers/webhook.js';
25
+ export { sendTestWebhook, publicBaseUrl } from '#core/http-notify-triggers/webhook.js';
25
26
  export { describeAgentNotifier, saveAgentNotifier, removeAgentNotifier, setAgentPrimaryNotifier, testAgentNotifier, listAgentPending, slackCredentialsFor, slackConversationsOn, slackClickAllowed, agentApprovers, setAgentApprovers, listAgentSlackMembers, listAgentSlackChannels, announceToNotifier } from './web/notifier-admin.js';
27
+ export { clickAllowed, conversationsOn, jobsOn, jobAllowed, connectionPublicUrl, telegramCredentialsFor, telegramHomeChat, listAgentTelegramPeople, setupAgentTelegramWebhook, agentTelegramWebhookInfo } from './web/notifier-admin.js';
28
+ export { createTelegramConnector, parseTelegramUpdate, verifyTelegramRequest, telegramCallbackData, answerTelegramClick, editTelegramMessage, telegramBotUsername, setTelegramWebhook, telegramWebhookInfo, callTelegram, recordTelegramMessage, readTelegramThread, telegramRootOf, telegramPeople, toTelegramHtml, telegramEscape, telegramArgs, } from '#core/messaging/connectors/telegram/index.js';
26
29
  // The framework-agnostic browser client for adapters/http.ts's own wire
27
30
  // protocol (see client.ts's own header comment) — this, not a deep
28
31
  // subpath import, is how a consumer's React/Vue/etc. chat UI is meant to