paperclip-plugin-mattermost 0.17.0 → 0.18.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 +2 -1
- package/dist/manifest.js +8 -6
- package/dist/worker.js +36 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,7 @@ Your [Paperclip](https://github.com/paperclipai/paperclip) agents as Mattermost
|
|
|
14
14
|
- **Single-choice questions** get a button per option. Your pick is posted on the task as a comment from you, which wakes the agent.
|
|
15
15
|
- **Access control.** Choose who may talk to agents: everyone, or only the people you pick. Each person can be limited to some agents, and people outside Paperclip can be added by email.
|
|
16
16
|
- **Agents work in Mattermost themselves.** Through plugin API routes that only agents can call, each agent reads the channels its bot is in, posts, reacts, searches, and DMs the people allowed to talk to it, always as its own bot. Each company gets a Mattermost skill that tells agents how, and saving the settings adds it to every agent with a bot, so they can message people from tasks and scheduled runs too.
|
|
17
|
+
- **Work happens in Paperclip tasks.** Asked to do something, an agent creates a Paperclip task, replies with its number, and does the work there. When the task is done, cancelled or blocked, its outcome is posted back in the thread it was asked in.
|
|
17
18
|
- **Steering.** A message you send in a thread while its agent is still working reaches that run: the agent reads it between steps (and an agent waiting at its computer stops waiting to read it), so "stop" or a correction lands mid-task. One it read is marked ✅ and not run again.
|
|
18
19
|
- **Agents work with agents.** An agent mentioning another agent starts it, and it answers in the thread. After a set number of agent-to-agent messages in a thread (10 by default, set in settings), they pause until a person replies.
|
|
19
20
|
- **Work channels.** For an investigation, incident or project, an agent opens a private channel (`inc-checkout-5xx`), pulls in the people and agents it needs, and ties it to a Paperclip task. Task comments and status changes show up in the channel. When the task is done or cancelled, the channel is archived and the outcome is posted back where the work was asked for.
|
|
@@ -84,7 +85,7 @@ PAPERCLIP_REF=<commit> node build.mjs # build for another Paperclip version
|
|
|
84
85
|
Bump `version` in `package.json` and `src/manifest.ts`, commit, then tag and push:
|
|
85
86
|
|
|
86
87
|
```bash
|
|
87
|
-
git tag -a v0.
|
|
88
|
+
git tag -a v0.18.0 -m v0.18.0 && git push origin main v0.18.0
|
|
88
89
|
```
|
|
89
90
|
|
|
90
91
|
GitHub Actions builds, tests and publishes to npm through npm trusted publishing, so no npm token is involved.
|
package/dist/manifest.js
CHANGED
|
@@ -16,11 +16,13 @@ tell whoever you are working with and carry on. A 403 says why something is not
|
|
|
16
16
|
|
|
17
17
|
## How to work
|
|
18
18
|
|
|
19
|
-
- **
|
|
20
|
-
task (assign it to yourself or delegate it) and say so in the thread
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
- **Work within Paperclip tasks.** A quick question: answer in the thread. Anything you are asked to
|
|
20
|
+
do: create a Paperclip task first (assign it to yourself or delegate it) and say so in the thread
|
|
21
|
+
with its number. Do the work in the task and end with the outcome as its last comment: when the
|
|
22
|
+
task is done, cancelled or blocked, that comment is posted back in the thread it was asked in. An
|
|
23
|
+
investigation, incident or project, or anything involving several agents or more than a day: also
|
|
24
|
+
open a work channel for it (below), with the person who asked and whoever you need. Name it for
|
|
25
|
+
what it is: "inc checkout 5xx", "inv q3 conversion drop", "proj crm migration".
|
|
24
26
|
- **Announce a work channel where the request came in**, in one message: what it is for, who you
|
|
25
27
|
pulled in, and that you will report back there. When the task is done or cancelled the plugin
|
|
26
28
|
archives the channel and posts the outcome back where you asked; you do not need to.
|
|
@@ -82,7 +84,7 @@ var route = (routeKey, method, path) => ({ routeKey, method, path, auth: "agent"
|
|
|
82
84
|
var manifest = {
|
|
83
85
|
id: "mattermost-bridge",
|
|
84
86
|
apiVersion: 1,
|
|
85
|
-
version: "0.
|
|
87
|
+
version: "0.18.0",
|
|
86
88
|
displayName: "Mattermost",
|
|
87
89
|
description: "Every agent becomes its own Mattermost bot. DM an agent or mention it in a channel and it answers in the thread, showing its steps as it works; follow-ups continue the conversation. Paperclip approvals arrive with Approve / Reject buttons.",
|
|
88
90
|
author: "Haseeb Azhar",
|
package/dist/worker.js
CHANGED
|
@@ -45353,6 +45353,7 @@ var Actions = class {
|
|
|
45353
45353
|
// src/worklinks.ts
|
|
45354
45354
|
var FROM_PAPERCLIP = { from_paperclip: "true" };
|
|
45355
45355
|
var CLOSED = /* @__PURE__ */ new Set(["done", "cancelled"]);
|
|
45356
|
+
var THREAD_REPORTS = { done: ":white_check_mark: Done:", cancelled: ":no_entry_sign: Cancelled:", blocked: ":pause_button: Blocked:" };
|
|
45356
45357
|
var STATUS_WORDS = {
|
|
45357
45358
|
todo: "to do",
|
|
45358
45359
|
in_progress: "in progress",
|
|
@@ -45388,6 +45389,25 @@ var WorkLinks = class {
|
|
|
45388
45389
|
${body}`;
|
|
45389
45390
|
await this.d.post(own2 ? c.authorAgentId : hit.link.agentId, hit.channelId, "", text2);
|
|
45390
45391
|
}
|
|
45392
|
+
/**
|
|
45393
|
+
* A task asked for in a Mattermost thread (created by the agent while answering there): when it is
|
|
45394
|
+
* done, cancelled or blocked, its outcome (the last comment) is posted back in that thread, so the
|
|
45395
|
+
* result always reaches the conversation it came from.
|
|
45396
|
+
*/
|
|
45397
|
+
async threadUpdate(issueId) {
|
|
45398
|
+
const at = await this.d.store.get(`issuethread:${issueId}`);
|
|
45399
|
+
if (!at) return;
|
|
45400
|
+
const task2 = await this.d.task(issueId);
|
|
45401
|
+
if (!task2 || task2.status === at.status || !THREAD_REPORTS[task2.status]) return;
|
|
45402
|
+
await this.d.store.set(`issuethread:${issueId}`, { ...at, status: task2.status });
|
|
45403
|
+
const last = (await this.d.comments(issueId)).at(-1)?.body?.trim();
|
|
45404
|
+
const outcome = last ? `
|
|
45405
|
+
|
|
45406
|
+
${last.length > 3e3 ? last.slice(0, 3e3) + "\u2026" : last}` : "";
|
|
45407
|
+
const by = [task2.assigneeAgentId, at.agentId].find((a) => Boolean(a && this.d.hasBot(a)));
|
|
45408
|
+
if (!by) return;
|
|
45409
|
+
await this.d.post(by, at.channelId, at.rootId, `${THREAD_REPORTS[task2.status]} **${task2.identifier ?? "The task"}** ${task2.title}${outcome}`);
|
|
45410
|
+
}
|
|
45391
45411
|
/** The task changed: note a new status; on done or cancelled, archive and report back. */
|
|
45392
45412
|
async updated(issueId) {
|
|
45393
45413
|
const hit = await this.linked(issueId);
|
|
@@ -45443,11 +45463,13 @@ tell whoever you are working with and carry on. A 403 says why something is not
|
|
|
45443
45463
|
|
|
45444
45464
|
## How to work
|
|
45445
45465
|
|
|
45446
|
-
- **
|
|
45447
|
-
task (assign it to yourself or delegate it) and say so in the thread
|
|
45448
|
-
|
|
45449
|
-
|
|
45450
|
-
|
|
45466
|
+
- **Work within Paperclip tasks.** A quick question: answer in the thread. Anything you are asked to
|
|
45467
|
+
do: create a Paperclip task first (assign it to yourself or delegate it) and say so in the thread
|
|
45468
|
+
with its number. Do the work in the task and end with the outcome as its last comment: when the
|
|
45469
|
+
task is done, cancelled or blocked, that comment is posted back in the thread it was asked in. An
|
|
45470
|
+
investigation, incident or project, or anything involving several agents or more than a day: also
|
|
45471
|
+
open a work channel for it (below), with the person who asked and whoever you need. Name it for
|
|
45472
|
+
what it is: "inc checkout 5xx", "inv q3 conversion drop", "proj crm migration".
|
|
45451
45473
|
- **Announce a work channel where the request came in**, in one message: what it is for, who you
|
|
45452
45474
|
pulled in, and that you will report back there. When the task is done or cancelled the plugin
|
|
45453
45475
|
archives the channel and posts the outcome back where you asked; you do not need to.
|
|
@@ -45750,7 +45772,7 @@ var Bridge = class {
|
|
|
45750
45772
|
const attached = fileIds.length ? await this.files.receive(member.chat, post.id, fileIds) : [];
|
|
45751
45773
|
const outbox = this.files ? await this.files.outbox(post.id).catch(() => null) : null;
|
|
45752
45774
|
const fromAgent = this.roster().isBot(post.user_id);
|
|
45753
|
-
const prompt = `${sender.displayName} (@${sender.username}${fromAgent ? ", another agent" : ""}) wrote to you (@${member.username}) in ${where} on Mattermost. Your reply is posted back there, so answer in Markdown. To offer choices, end your reply with one line \`choices: First | Second\` (2 to ${MAX_CHOICES} short options); they are shown as buttons, and the pick comes back as the next message. You can also read channels, post, message people, mention other agents and open work channels in Mattermost: GET <your Paperclip API URL>/api/plugins/mattermost-bridge/api/mm/help shows how. If this takes more than a minute or two, check GET \u2026/api/plugins/mattermost-bridge/api/mm/inbox between steps: it returns anything people write in this thread meanwhile (a correction, "stop", "never mind"); act on it then, as it is not sent to you again.` + (history ? `
|
|
45775
|
+
const prompt = `${sender.displayName} (@${sender.username}${fromAgent ? ", another agent" : ""}) wrote to you (@${member.username}) in ${where} on Mattermost. Your reply is posted back there, so answer in Markdown. Work within Paperclip tasks: if the message asks you to do something (anything more than answering a quick question), first create a Paperclip task for it, assigned to yourself or to the agent it belongs with, and reply here with the task's number, what will happen, and that the result will come back to this thread. Then do the work in that task, not in this chat, and finish with the outcome as the task's last comment: when the task is done, cancelled or blocked, that comment is posted back here for you. Quick questions you can answer directly. To offer choices, end your reply with one line \`choices: First | Second\` (2 to ${MAX_CHOICES} short options); they are shown as buttons, and the pick comes back as the next message. You can also read channels, post, message people, mention other agents and open work channels in Mattermost: GET <your Paperclip API URL>/api/plugins/mattermost-bridge/api/mm/help shows how. If this takes more than a minute or two, check GET \u2026/api/plugins/mattermost-bridge/api/mm/inbox between steps: it returns anything people write in this thread meanwhile (a correction, "stop", "never mind"); act on it then, as it is not sent to you again.` + (history ? `
|
|
45754
45776
|
|
|
45755
45777
|
Earlier in this thread (oldest first):
|
|
45756
45778
|
${history}
|
|
@@ -46283,7 +46305,7 @@ var route = (routeKey, method, path3) => ({ routeKey, method, path: path3, auth:
|
|
|
46283
46305
|
var manifest = {
|
|
46284
46306
|
id: "mattermost-bridge",
|
|
46285
46307
|
apiVersion: 1,
|
|
46286
|
-
version: "0.
|
|
46308
|
+
version: "0.18.0",
|
|
46287
46309
|
displayName: "Mattermost",
|
|
46288
46310
|
description: "Every agent becomes its own Mattermost bot. DM an agent or mention it in a channel and it answers in the thread, showing its steps as it works; follow-ups continue the conversation. Paperclip approvals arrive with Approve / Reject buttons.",
|
|
46289
46311
|
author: "Haseeb Azhar",
|
|
@@ -46637,7 +46659,8 @@ var Tenant = class {
|
|
|
46637
46659
|
store,
|
|
46638
46660
|
post: async (agentId, channelId, rootId, text2) => {
|
|
46639
46661
|
const as = roster.clientOf(agentId) ?? manager;
|
|
46640
|
-
|
|
46662
|
+
const message2 = linkTasks(absoluteLinks(text2, paperclipUrl), paperclipUrl, bridge.taskPrefix);
|
|
46663
|
+
await as.request("POST", "/posts", { channel_id: channelId, root_id: rootId, message: message2, props: FROM_PAPERCLIP });
|
|
46641
46664
|
},
|
|
46642
46665
|
hasBot: (agentId) => Boolean(roster.clientOf(agentId)),
|
|
46643
46666
|
agentName: (agentId) => agentName(agentId),
|
|
@@ -46844,7 +46867,10 @@ var plugin = definePlugin({
|
|
|
46844
46867
|
if (e.entityId && commentId) await tenants.get(e.companyId)?.work?.comment(e.entityId, commentId);
|
|
46845
46868
|
}));
|
|
46846
46869
|
ctx.events.on("issue.updated", (e) => quietly("could not follow a task update", async () => {
|
|
46847
|
-
if (e.entityId)
|
|
46870
|
+
if (!e.entityId) return;
|
|
46871
|
+
const work = tenants.get(e.companyId)?.work;
|
|
46872
|
+
await work?.updated(e.entityId);
|
|
46873
|
+
await work?.threadUpdate(e.entityId);
|
|
46848
46874
|
}));
|
|
46849
46875
|
ctx.events.on("plugin.agent-computer.needs-person", (e) => quietly("could not post a computer help request", async () => {
|
|
46850
46876
|
await tenants.get(e.companyId)?.computerNeedsPerson(e.payload ?? {});
|
|
@@ -46855,7 +46881,7 @@ var plugin = definePlugin({
|
|
|
46855
46881
|
const issue2 = await ctx.issues.get(e.entityId, e.companyId);
|
|
46856
46882
|
const by = issue2?.createdByAgentId;
|
|
46857
46883
|
const at = by ? tenant.bridge.activeThread(by) : null;
|
|
46858
|
-
if (at) await tenant.store().set(`issuethread:${e.entityId}`, at);
|
|
46884
|
+
if (at) await tenant.store().set(`issuethread:${e.entityId}`, { ...at, agentId: by, status: issue2?.status });
|
|
46859
46885
|
}));
|
|
46860
46886
|
ctx.events.on("approval.decided", (e) => quietly("could not update approval", async () => {
|
|
46861
46887
|
if (e.entityId) await tenants.get(e.companyId)?.approvals?.decided(e.entityId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperclip-plugin-mattermost",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Paperclip agents in Mattermost: a bot per agent, live progress, files, approval and decision buttons, and per-person access, for each Paperclip company.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|