@stratusagent/channel-slack 0.3.0 → 0.6.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.
- package/README.md +66 -0
- package/dist/index.d.ts +54 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +521 -4
- package/dist/index.js.map +1 -1
- package/manifest/stratus-agent.manifest.json +1 -1
- package/package.json +4 -3
- package/dist/tsconfig.test.tsbuildinfo +0 -1
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ Slack adapter for Stratus agents. **One Slack app per agent** — Slack has no w
|
|
|
5
5
|
- **Resumable conversations**: session keys are `slack:<agent>:<team>:<channel>:<thread_ts ?? ts>` (DMs: the DM channel id) — a thread is a conversation, it survives daemon restarts, and two agents sharing a thread keep fully separate sessions.
|
|
6
6
|
- **Streaming replies**: post a placeholder, edit as deltas arrive (throttled for `chat.update` limits), show `⚙ tool…` status lines, finalize with the full reply — split across messages when it outgrows one.
|
|
7
7
|
- **Mention-only in channels, free-form in DMs.** Inbound mentions of other users are humanized to `@Display Name` for the model; redeliveries are deduped so a slow turn never runs twice.
|
|
8
|
+
- **Approval buttons** for the gateway's `remote` permission mode: a gated tool call parks the turn and asks in its thread with **Allow once** / **Always allow** / **Deny**. See [Approving tool calls](#approving-tool-calls) — clicks are authorized by *who clicked*, never by who can see the message.
|
|
8
9
|
- **Tokens are gateway infrastructure secrets**, stored in the `channels` namespace of `~/.stratus/credentials.json` — never in an agent's credential allowlist:
|
|
9
10
|
|
|
10
11
|
```json
|
|
@@ -45,3 +46,68 @@ The manual equivalent, if you prefer:
|
|
|
45
46
|
3. **Install App** to the workspace → copy the **Bot User OAuth Token** (that's the `botToken`, `xoxb-…`).
|
|
46
47
|
4. Upload the agent's avatar under **Display Information**.
|
|
47
48
|
5. Add both tokens under `channels.slack.<agentId>` in `~/.stratus/credentials.json` and restart `stratus serve` — the log will show `slack: <agentId> connected`.
|
|
49
|
+
|
|
50
|
+
## Approving tool calls
|
|
51
|
+
|
|
52
|
+
When `stratus serve` runs with `approvals.mode: "remote"`, a gated tool call
|
|
53
|
+
parks the turn and the adapter asks here. The question goes to the thread the
|
|
54
|
+
turn is happening in; a turn with no Slack conversation of its own (scheduled
|
|
55
|
+
work, a delegate) asks in the configured `slackChannel`.
|
|
56
|
+
|
|
57
|
+
Who may answer is configured per agent, in `~/.stratus/config.json`:
|
|
58
|
+
|
|
59
|
+
```jsonc
|
|
60
|
+
{
|
|
61
|
+
"approvals": {
|
|
62
|
+
"mode": "remote",
|
|
63
|
+
"slackChannel": "C07OPS",
|
|
64
|
+
"agents": {
|
|
65
|
+
"ava": { "slackApprovers": ["U01DYLAN", "U01OPS"] }
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
That block is only read from a config you chose — `--config`,
|
|
72
|
+
`STRATUS_CONFIG`, or the global `~/.stratus/config.json`. A project-local
|
|
73
|
+
`stratus.config.json` cannot appoint approvers, since it can be checked into
|
|
74
|
+
any repository.
|
|
75
|
+
|
|
76
|
+
**Clicks are authorized by actor, not by delivery.** Everyone in a thread can
|
|
77
|
+
see the request; only the ids listed for that agent can decide it. Anyone
|
|
78
|
+
else's click is refused with a notice only they see, and the request stays
|
|
79
|
+
open for someone who may actually answer — which matters most for **Always
|
|
80
|
+
allow**, since that widens what the agent may do for the rest of the session.
|
|
81
|
+
An agent with no approvers listed denies every request on arrival rather than
|
|
82
|
+
leaving it hanging — set `"slackApprovers": []` on an agent to exclude it
|
|
83
|
+
from a shared default list. The same goes for an agent whose Slack app is
|
|
84
|
+
configured here but failed to connect. An agent this adapter was never given
|
|
85
|
+
is left alone: a request is a broadcast, and refusing one another channel was
|
|
86
|
+
about to answer is not the adapter's call — `stratus serve` reports agents no
|
|
87
|
+
channel can ask for, at startup. Those automatic denials are recorded as
|
|
88
|
+
`undeliverable` rather than `decided`, so the log never shows a refusal
|
|
89
|
+
nobody made as one somebody did.
|
|
90
|
+
|
|
91
|
+
The message shows the tool's arguments as well as its name — approving
|
|
92
|
+
`shell.run` without seeing what it would run is not approval. Arguments are
|
|
93
|
+
escaped, so a model-written argument cannot mention or broadcast to the
|
|
94
|
+
workspace through the prompt itself, and long ones are truncated with a
|
|
95
|
+
notice saying so.
|
|
96
|
+
|
|
97
|
+
Requests are also denied, visibly, when they expire, when the turn is
|
|
98
|
+
cancelled, and when the daemon shuts down. Every ending the daemon is alive
|
|
99
|
+
for retracts the buttons, so a message does not keep offering a decision that
|
|
100
|
+
has nowhere to land.
|
|
101
|
+
|
|
102
|
+
A crash is the ending it cannot be alive for, and the record of what was
|
|
103
|
+
posted lives only in memory — so a new process cannot find its predecessor's
|
|
104
|
+
prompts to retract them. Those correct themselves on the next click: it
|
|
105
|
+
answers that the request is no longer pending and rewrites the message, since
|
|
106
|
+
the click is the one thing that carries the message's location back. A prompt
|
|
107
|
+
nobody clicks stays as it was until someone does.
|
|
108
|
+
|
|
109
|
+
**Interactivity must be enabled on the app** or Slack delivers no clicks at
|
|
110
|
+
all. The shipped manifest turns it on; an app created before this shipped
|
|
111
|
+
needs `settings.interactivity.is_enabled` set to `true` once, under **App
|
|
112
|
+
Manifest**. No request URL is needed — the clicks arrive over the same Socket
|
|
113
|
+
Mode connection.
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,22 @@ export interface SlackAgentConfig {
|
|
|
4
4
|
agentId: string;
|
|
5
5
|
appToken: string;
|
|
6
6
|
botToken: string;
|
|
7
|
+
/**
|
|
8
|
+
* Slack user ids allowed to answer this agent's approval requests.
|
|
9
|
+
* Authorization is by ACTOR, never by delivery: a request posted into a
|
|
10
|
+
* shared thread is visible to everyone there, and one of them clicking
|
|
11
|
+
* **Always allow** would otherwise widen what the agent may do unattended
|
|
12
|
+
* for the rest of the session. Empty (or absent) means nobody can
|
|
13
|
+
* approve, so requests are declined on arrival rather than left hanging.
|
|
14
|
+
*/
|
|
15
|
+
approvers?: string[];
|
|
16
|
+
/**
|
|
17
|
+
* Where to ask when the turn is not itself happening in Slack — a
|
|
18
|
+
* schedule-driven turn, a delegate, an HTTP dispatch. Defaults to the
|
|
19
|
+
* conversation the turn came from, which is where the person waiting on
|
|
20
|
+
* the answer already is.
|
|
21
|
+
*/
|
|
22
|
+
approvalChannel?: string;
|
|
7
23
|
}
|
|
8
24
|
export interface SlackSocketEventArgs {
|
|
9
25
|
ack: (response?: unknown) => Promise<void>;
|
|
@@ -11,10 +27,36 @@ export interface SlackSocketEventArgs {
|
|
|
11
27
|
body?: {
|
|
12
28
|
team_id?: string;
|
|
13
29
|
event_id?: string;
|
|
30
|
+
/** block_actions only: who clicked. The one field authorization reads. */
|
|
31
|
+
user?: {
|
|
32
|
+
id?: string;
|
|
33
|
+
};
|
|
34
|
+
actions?: SlackBlockAction[];
|
|
35
|
+
channel?: {
|
|
36
|
+
id?: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* block_actions only: the message the click came from, as Slack holds
|
|
40
|
+
* it at the moment it processes the interaction — so `blocks` is the
|
|
41
|
+
* message's real current state, not what the clicking client had
|
|
42
|
+
* rendered.
|
|
43
|
+
*/
|
|
44
|
+
message?: {
|
|
45
|
+
ts?: string;
|
|
46
|
+
thread_ts?: string;
|
|
47
|
+
blocks?: SlackBlock[];
|
|
48
|
+
};
|
|
14
49
|
};
|
|
15
50
|
event?: SlackInboundEvent;
|
|
16
51
|
retry_num?: number;
|
|
17
52
|
}
|
|
53
|
+
export interface SlackBlockAction {
|
|
54
|
+
action_id?: string;
|
|
55
|
+
/** The request id the button carries. */
|
|
56
|
+
value?: string;
|
|
57
|
+
}
|
|
58
|
+
/** Block Kit is structural, not typed here beyond what the adapter emits. */
|
|
59
|
+
export type SlackBlock = Record<string, unknown>;
|
|
18
60
|
export interface SlackInboundEvent {
|
|
19
61
|
type: string;
|
|
20
62
|
user?: string;
|
|
@@ -43,6 +85,7 @@ export interface SlackWebLike {
|
|
|
43
85
|
channel: string;
|
|
44
86
|
text: string;
|
|
45
87
|
thread_ts?: string;
|
|
88
|
+
blocks?: SlackBlock[];
|
|
46
89
|
}): Promise<{
|
|
47
90
|
ts?: string;
|
|
48
91
|
channel?: string;
|
|
@@ -51,6 +94,17 @@ export interface SlackWebLike {
|
|
|
51
94
|
channel: string;
|
|
52
95
|
ts: string;
|
|
53
96
|
text: string;
|
|
97
|
+
blocks?: SlackBlock[];
|
|
98
|
+
}): Promise<unknown>;
|
|
99
|
+
/**
|
|
100
|
+
* Visible only to `user`. Refusing a click needs to reach the person
|
|
101
|
+
* who clicked without announcing to the channel that they tried.
|
|
102
|
+
*/
|
|
103
|
+
postEphemeral(args: {
|
|
104
|
+
channel: string;
|
|
105
|
+
user: string;
|
|
106
|
+
text: string;
|
|
107
|
+
thread_ts?: string;
|
|
54
108
|
}): Promise<unknown>;
|
|
55
109
|
};
|
|
56
110
|
files: {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,cAAc,EAGpB,MAAM,wBAAwB,CAAC;AAOhC,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|