cyrus-edge-worker 0.2.61 → 0.2.63

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.
@@ -1,5 +1,25 @@
1
1
  import { createLogger } from "cyrus-core";
2
2
  import { SlackMessageService, SlackReactionService, stripMention as stripSlackMention, } from "cyrus-slack-event-transport";
3
+ /**
4
+ * Sentinel the agent emits when it has decided a Slack message does not warrant
5
+ * a reply. `postReply` recognizes it and stays silent instead of posting.
6
+ *
7
+ * This is what makes the "only respond when relevant" policy in the system
8
+ * prompt actually take effect: because every completed turn would otherwise be
9
+ * posted back to the thread, the agent needs an explicit way to say "nothing to
10
+ * post here". Kept as a single constant so the prompt and the suppression check
11
+ * can never drift apart.
12
+ */
13
+ export const SLACK_NO_RESPONSE_SENTINEL = "<<NO_RESPONSE>>";
14
+ /**
15
+ * Route of the hosted Behaviours settings page (relative to the Cyrus app
16
+ * base URL) where automatic Slack thread listening can be turned off.
17
+ */
18
+ export const BEHAVIOURS_PAGE_ROUTE = "/settings/behaviours";
19
+ /** Reaction added when a message is received and queued for processing (👀) */
20
+ export const RECEIPT_REACTION = "eyes";
21
+ /** Reaction that replaces the receipt one once the agent finished its turn (✅) */
22
+ export const PROCESSED_REACTION = "white_check_mark";
3
23
  /**
4
24
  * Slack implementation of ChatPlatformAdapter.
5
25
  *
@@ -11,12 +31,17 @@ export class SlackChatAdapter {
11
31
  platformName = "slack";
12
32
  repositoryProvider;
13
33
  repositoryRoutingContext;
34
+ behavioursPageUrl;
14
35
  logger;
15
36
  selfBotId;
16
37
  constructor(repositoryProvider, logger, options) {
17
38
  this.repositoryProvider = repositoryProvider;
18
39
  this.repositoryRoutingContext =
19
40
  options?.repositoryRoutingContext?.trim() || "";
41
+ const appBaseUrl = options?.cyrusAppBaseUrl?.trim().replace(/\/+$/, "");
42
+ this.behavioursPageUrl = appBaseUrl
43
+ ? `${appBaseUrl}${BEHAVIOURS_PAGE_ROUTE}`
44
+ : "";
20
45
  this.logger = logger ?? createLogger({ component: "SlackChatAdapter" });
21
46
  }
22
47
  /**
@@ -48,6 +73,23 @@ export class SlackChatAdapter {
48
73
  extractTaskInstructions(event) {
49
74
  return (stripSlackMention(event.payload.text) || "Ask the user for more context");
50
75
  }
76
+ /**
77
+ * Decide whether an event may start a session when the runtime has no
78
+ * in-memory binding for its thread.
79
+ *
80
+ * - An explicit @mention always may.
81
+ * - A plain `message` event may only when it was upstream-gated (proxy mode):
82
+ * CYHOST forwards `message` events solely for threads it has a persistent
83
+ * binding row for, so reaching us means the thread is genuinely bound. This
84
+ * is what lets Cyrus keep answering follow-ups after a process restart wipes
85
+ * the in-memory binding — the prior Slack thread is rehydrated via
86
+ * `fetchThreadContext`. In direct mode (`upstreamGated` false) there is no
87
+ * such guarantee, so an unbound plain message is ignored to avoid starting a
88
+ * session for arbitrary channel chatter.
89
+ */
90
+ isSessionInitiatingEvent(event) {
91
+ return event.eventType === "app_mention" || event.upstreamGated === true;
92
+ }
51
93
  getThreadKey(event) {
52
94
  const threadTs = event.payload.thread_ts || event.payload.ts;
53
95
  return `${event.payload.channel}:${threadTs}`;
@@ -72,12 +114,30 @@ ${repositoryPaths.map((path) => `- ${path}`).join("\n")}
72
114
  : `
73
115
  ## Repository Access
74
116
  - No repository paths are configured for this chat session.`;
75
- return `You are responding to a Slack @mention.
117
+ const stopListeningSection = this.behavioursPageUrl
118
+ ? `
119
+
120
+ ## Stopping Automatic Listening
121
+ - If the user asks you to stop listening to, following, or responding in this thread:
122
+ - Tell them automatic thread listening can be turned off on the Behaviours page: <${this.behavioursPageUrl}|Behaviours page>.
123
+ - From that point on, treat this thread as muted: stay silent (emit \`${SLACK_NO_RESPONSE_SENTINEL}\` and nothing else) for every subsequent message until someone asks you a direct question — addressing you by name ("Cyrus, …") or with an @mention. When you resume responding, just answer — do not announce that you are listening again.`
124
+ : "";
125
+ return `You are participating in a Slack thread.
76
126
 
77
127
  ## Context
78
128
  - **Requested by**: ${event.payload.user}
79
129
  - **Channel**: ${event.payload.channel}
80
130
 
131
+ ## When to Respond (IMPORTANT)
132
+ - After you are first @mentioned, you receive **every** subsequent message in this thread, not just the ones aimed at you. Do not treat every message as a request for you.
133
+ - Respond ONLY when at least one of these is true:
134
+ 1. The message asks a question you can genuinely and helpfully answer, OR
135
+ 2. Someone addresses you directly — by name ("Cyrus, …") or with an @mention.
136
+ - For anything else — side conversation between people, acknowledgements ("thanks", "👍"), status chatter, or messages clearly not directed at you — do NOT reply.
137
+ - When you should stay silent, output exactly \`${SLACK_NO_RESPONSE_SENTINEL}\` and nothing else — no reasoning, no explanation, not a single word before or after the token.
138
+ - NEVER narrate your decision about whether to respond. Your entire output is posted verbatim to the thread — there is no private scratchpad. Thoughts like "the user didn't address me by name, so I should stay quiet" or "they addressed me by name, so I'm listening again" must never appear in your output. Either emit the bare token, or reply directly to the user's message as if the decision never happened.
139
+ - When you do respond, be genuinely helpful and concise.${stopListeningSection}
140
+
81
141
  ## Instructions
82
142
  - You are running in a transient workspace, not associated with any code repository
83
143
  - Be concise in your responses as they will be posted back to Slack
@@ -171,6 +231,17 @@ Supported mrkdwn syntax:
171
231
  summary = textBlock.text;
172
232
  }
173
233
  }
234
+ // The agent emits the no-response sentinel when it judged this message
235
+ // didn't warrant a reply (see the "When to Respond" system prompt
236
+ // section). Honor that by posting nothing. Deliberately a substring
237
+ // check, not an exact match: agents sometimes narrate their reasoning
238
+ // around the token despite being told not to, and that deliberation
239
+ // must never reach the thread — the token's presence anywhere means
240
+ // "do not post".
241
+ if (summary.includes(SLACK_NO_RESPONSE_SENTINEL)) {
242
+ this.logger.info(`Slack agent opted not to respond in channel ${event.payload.channel} (no-response sentinel)`);
243
+ return;
244
+ }
174
245
  const token = this.getSlackBotToken(event);
175
246
  if (!token) {
176
247
  this.logger.warn("Cannot post Slack reply: no slackBotToken available");
@@ -200,9 +271,33 @@ Supported mrkdwn syntax:
200
271
  token,
201
272
  channel: event.payload.channel,
202
273
  timestamp: event.payload.ts,
203
- name: "eyes",
274
+ name: RECEIPT_REACTION,
204
275
  });
205
276
  }
277
+ /**
278
+ * Swap the receipt reaction (👀) for a processed one (✅) once the agent
279
+ * has finished its turn for this message. This runs whether or not a reply
280
+ * was posted, so users can tell a silently-skipped message was still seen.
281
+ */
282
+ async acknowledgeProcessed(event) {
283
+ const token = this.getSlackBotToken(event);
284
+ if (!token) {
285
+ this.logger.warn("Cannot update Slack reaction: no slackBotToken available (SLACK_BOT_TOKEN env var not set)");
286
+ return;
287
+ }
288
+ const reactionService = new SlackReactionService();
289
+ const target = {
290
+ token,
291
+ channel: event.payload.channel,
292
+ timestamp: event.payload.ts,
293
+ };
294
+ // Remove the receipt reaction before adding the processed one so the
295
+ // two are never visible together — the swap reads as a clean
296
+ // transition. (Slack has no atomic swap; if the add fails the message
297
+ // is briefly indicator-less, which beats showing both.)
298
+ await reactionService.removeReaction({ ...target, name: RECEIPT_REACTION });
299
+ await reactionService.addReaction({ ...target, name: PROCESSED_REACTION });
300
+ }
206
301
  async notifyBusy(event) {
207
302
  const token = this.getSlackBotToken(event);
208
303
  if (!token) {
@@ -1 +1 @@
1
- {"version":3,"file":"SlackChatAdapter.js","sourceRoot":"","sources":["../src/SlackChatAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SlackChatAdapter.js","sourceRoot":"","sources":["../src/SlackChatAdapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Optional system-prompt addendum for Cyrus-managed cloud runtimes.
3
+ *
4
+ * On our managed cloud runtimes the dev environment's system-wide packages
5
+ * (`apt` / global `npm`) are provisioned out-of-band from a curated list the
6
+ * user controls at https://app.atcyrus.com/settings/packages. When the agent
7
+ * finds a package missing, installing it ad-hoc inside the session won't
8
+ * persist and may fail on permissions — the correct remedy is to tell the user
9
+ * to add it via that settings page.
10
+ *
11
+ * Only injected when the environment variable `CYRUS_CLOUD_RUNTIME` is set to a
12
+ * truthy value. cyrus-hosted sets this on cloud-runtime droplets and leaves it
13
+ * unset for self-host runtimes (where the user manages their own packages).
14
+ */
15
+ export declare const CLOUD_RUNTIME_PROMPT_ADDENDUM: string;
16
+ /**
17
+ * Append the cloud-runtime addendum to a system prompt fragment, but only when
18
+ * the `CYRUS_CLOUD_RUNTIME` env var is truthy. Returns the existing prompt
19
+ * unchanged otherwise.
20
+ */
21
+ export declare function appendCloudRuntimeAddendum(existing: string | undefined | null): string;
22
+ //# sourceMappingURL=cloudRuntimePromptAddendum.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudRuntimePromptAddendum.d.ts","sourceRoot":"","sources":["../../src/prompts/cloudRuntimePromptAddendum.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,6BAA6B,QAoBlC,CAAC;AAET;;;;GAIG;AACH,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GACjC,MAAM,CAOR"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Optional system-prompt addendum for Cyrus-managed cloud runtimes.
3
+ *
4
+ * On our managed cloud runtimes the dev environment's system-wide packages
5
+ * (`apt` / global `npm`) are provisioned out-of-band from a curated list the
6
+ * user controls at https://app.atcyrus.com/settings/packages. When the agent
7
+ * finds a package missing, installing it ad-hoc inside the session won't
8
+ * persist and may fail on permissions — the correct remedy is to tell the user
9
+ * to add it via that settings page.
10
+ *
11
+ * Only injected when the environment variable `CYRUS_CLOUD_RUNTIME` is set to a
12
+ * truthy value. cyrus-hosted sets this on cloud-runtime droplets and leaves it
13
+ * unset for self-host runtimes (where the user manages their own packages).
14
+ */
15
+ export const CLOUD_RUNTIME_PROMPT_ADDENDUM = `
16
+ <cloud_runtime_packages>
17
+ You are running on a Cyrus-managed cloud runtime. The system-wide packages
18
+ available in this environment (\`apt\` packages and global \`npm\` packages) can be
19
+ extended in the Cyrus dashboard.
20
+
21
+ If you discover that a system package, tool, or CLI binary you need is **not
22
+ installed** (for example \`command not found\`, a missing \`apt\` package, or a
23
+ missing global \`npm\` package), do NOT try to install it yourself with
24
+ \`sudo apt install\` / \`npm install -g\` — changes like that will not persist
25
+ across runtime restarts and may fail due to permissions.
26
+
27
+ Instead:
28
+ - Tell the user to visit https://app.atcyrus.com/settings/packages and add the
29
+ required \`apt\` or \`npm\` package(s) there.
30
+ - Name the exact package(s) you need and briefly explain why.
31
+ - Inform the user that after they have installed the packages, they may reprompt in order to continue the work, unblocked.
32
+ - Continue with any work you can complete without the missing package, and
33
+ clearly call out what remains blocked until they install it.
34
+ </cloud_runtime_packages>
35
+ `.trim();
36
+ /**
37
+ * Append the cloud-runtime addendum to a system prompt fragment, but only when
38
+ * the `CYRUS_CLOUD_RUNTIME` env var is truthy. Returns the existing prompt
39
+ * unchanged otherwise.
40
+ */
41
+ export function appendCloudRuntimeAddendum(existing) {
42
+ const base = (existing ?? "").trimEnd();
43
+ if (!isCloudRuntimeEnabled()) {
44
+ return existing ?? "";
45
+ }
46
+ if (base.length === 0)
47
+ return CLOUD_RUNTIME_PROMPT_ADDENDUM;
48
+ return `${base}\n\n${CLOUD_RUNTIME_PROMPT_ADDENDUM}`;
49
+ }
50
+ function isCloudRuntimeEnabled() {
51
+ const raw = process.env.CYRUS_CLOUD_RUNTIME;
52
+ if (!raw)
53
+ return false;
54
+ const normalized = raw.trim().toLowerCase();
55
+ return normalized === "1" || normalized === "true" || normalized === "yes";
56
+ }
57
+ //# sourceMappingURL=cloudRuntimePromptAddendum.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudRuntimePromptAddendum.js","sourceRoot":"","sources":["../../src/prompts/cloudRuntimePromptAddendum.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG;;;;;;;;;;;;;;;;;;;;CAoB5C,CAAC,IAAI,EAAE,CAAC;AAET;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CACzC,QAAmC;IAEnC,MAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IACxC,IAAI,CAAC,qBAAqB,EAAE,EAAE,CAAC;QAC9B,OAAO,QAAQ,IAAI,EAAE,CAAC;IACvB,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,6BAA6B,CAAC;IAC5D,OAAO,GAAG,IAAI,OAAO,6BAA6B,EAAE,CAAC;AACtD,CAAC;AAED,SAAS,qBAAqB;IAC7B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;IAC5C,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAC;IACvB,MAAM,UAAU,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5C,OAAO,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,MAAM,IAAI,UAAU,KAAK,KAAK,CAAC;AAC5E,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyrus-edge-worker",
3
- "version": "0.2.61",
3
+ "version": "0.2.63",
4
4
  "description": "Unified edge worker for processing Linear issues with Claude",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -13,7 +13,7 @@
13
13
  "prompts"
14
14
  ],
15
15
  "dependencies": {
16
- "@anthropic-ai/claude-agent-sdk": "0.3.159",
16
+ "@anthropic-ai/claude-agent-sdk": "0.3.170",
17
17
  "@linear/sdk": "^64.0.0",
18
18
  "@ngrok/ngrok": "^1.5.1",
19
19
  "chokidar": "^4.0.3",
@@ -23,27 +23,27 @@
23
23
  "ignore": "^7.0.5",
24
24
  "node-forge": "^1.4.0",
25
25
  "zod": "4.3.6",
26
- "cyrus-claude-runner": "0.2.61",
27
- "cyrus-cloudflare-tunnel-client": "0.2.61",
28
- "cyrus-core": "0.2.61",
29
- "cyrus-config-updater": "0.2.61",
30
- "cyrus-codex-runner": "0.2.61",
31
- "cyrus-gemini-runner": "0.2.61",
32
- "cyrus-github-event-transport": "0.2.61",
33
- "cyrus-linear-event-transport": "0.2.61",
34
- "cyrus-slack-event-transport": "0.2.61",
35
- "cyrus-cursor-runner": "0.2.61",
36
- "cyrus-mcp-tools": "0.2.61",
37
- "cyrus-gitlab-event-transport": "0.2.61",
38
- "cyrus-simple-agent-runner": "0.2.61"
26
+ "cyrus-claude-runner": "0.2.63",
27
+ "cyrus-cloudflare-tunnel-client": "0.2.63",
28
+ "cyrus-config-updater": "0.2.63",
29
+ "cyrus-codex-runner": "0.2.63",
30
+ "cyrus-core": "0.2.63",
31
+ "cyrus-cursor-runner": "0.2.63",
32
+ "cyrus-github-event-transport": "0.2.63",
33
+ "cyrus-simple-agent-runner": "0.2.63",
34
+ "cyrus-gitlab-event-transport": "0.2.63",
35
+ "cyrus-mcp-tools": "0.2.63",
36
+ "cyrus-linear-event-transport": "0.2.63",
37
+ "cyrus-gemini-runner": "0.2.63",
38
+ "cyrus-slack-event-transport": "0.2.63"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^20.0.0",
42
42
  "@types/node-forge": "^1.3.14",
43
- "@vitest/coverage-v8": "^3.1.4",
43
+ "@vitest/coverage-v8": "^4.1.0",
44
44
  "axios": "^1.16.0",
45
45
  "typescript": "^5.3.3",
46
- "vitest": "^3.1.4",
46
+ "vitest": "^4.1.0",
47
47
  "vitest-mock-extended": "^3.1.0"
48
48
  },
49
49
  "publishConfig": {