paperclip-plugin-google-chat 0.1.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/LICENSE +21 -0
- package/README.md +94 -0
- package/dist/commands.d.ts +70 -0
- package/dist/commands.js +105 -0
- package/dist/config.d.ts +137 -0
- package/dist/config.js +73 -0
- package/dist/constants.d.ts +33 -0
- package/dist/constants.js +33 -0
- package/dist/events.d.ts +24 -0
- package/dist/events.js +49 -0
- package/dist/google-chat.d.ts +53 -0
- package/dist/google-chat.js +72 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/manifest.d.ts +167 -0
- package/dist/manifest.js +110 -0
- package/dist/tools.d.ts +28 -0
- package/dist/tools.js +71 -0
- package/dist/worker.d.ts +14 -0
- package/dist/worker.js +155 -0
- package/package.json +58 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Chat outbound client + pure formatting helpers.
|
|
3
|
+
*
|
|
4
|
+
* Outbound delivery uses Google Chat **incoming webhooks**: an HTTPS POST of a
|
|
5
|
+
* message resource to a per-space URL. The simplest body is `{ text }`; richer
|
|
6
|
+
* messages use Cards v2 (`cardsV2`). We keep the HTTP call behind a small
|
|
7
|
+
* `HttpFetch` seam so the formatters and routing can be unit-tested without the
|
|
8
|
+
* Paperclip SDK runtime.
|
|
9
|
+
*
|
|
10
|
+
* Docs: https://developers.google.com/workspace/chat/quickstart/webhooks
|
|
11
|
+
*/
|
|
12
|
+
/** Minimal fetch shape compatible with `ctx.http.fetch` and the global `fetch`. */
|
|
13
|
+
export type HttpFetch = (url: string, init?: {
|
|
14
|
+
method?: string;
|
|
15
|
+
headers?: Record<string, string>;
|
|
16
|
+
body?: string;
|
|
17
|
+
}) => Promise<{
|
|
18
|
+
ok: boolean;
|
|
19
|
+
status: number;
|
|
20
|
+
text(): Promise<string>;
|
|
21
|
+
}>;
|
|
22
|
+
export type RouteKey = "default" | "approvals" | "errors" | "digest";
|
|
23
|
+
export interface GoogleChatMessage {
|
|
24
|
+
text?: string;
|
|
25
|
+
cardsV2?: unknown[];
|
|
26
|
+
/** Thread key for grouping replies in a space. */
|
|
27
|
+
threadKey?: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* POST a message to a Google Chat space via its incoming-webhook URL.
|
|
31
|
+
* Returns the parsed result; throws on transport failure so callers can log.
|
|
32
|
+
*/
|
|
33
|
+
export declare function postToWebhook(fetchImpl: HttpFetch, webhookUrl: string, message: GoogleChatMessage): Promise<{
|
|
34
|
+
ok: boolean;
|
|
35
|
+
status: number;
|
|
36
|
+
body: string;
|
|
37
|
+
}>;
|
|
38
|
+
/** Append query params without clobbering existing ones. Exported for tests. */
|
|
39
|
+
export declare function appendQuery(url: string, params: Record<string, string>): string;
|
|
40
|
+
/** Google Chat supports a limited Markdown: *bold*, _italic_, `code`. */
|
|
41
|
+
export declare function bold(s: string): string;
|
|
42
|
+
export declare function link(text: string, url: string): string;
|
|
43
|
+
export interface IssueLike {
|
|
44
|
+
id?: string;
|
|
45
|
+
title?: string;
|
|
46
|
+
status?: string;
|
|
47
|
+
url?: string;
|
|
48
|
+
}
|
|
49
|
+
export declare function formatIssueCreated(issue: IssueLike): string;
|
|
50
|
+
export declare function formatIssueCompleted(issue: IssueLike): string;
|
|
51
|
+
export declare function formatApprovalRequested(issue: IssueLike): string;
|
|
52
|
+
export declare function formatAgentRunFailed(agentName: string, errorMsg: string): string;
|
|
53
|
+
export declare function formatBriefing(title: string | undefined, body: string): string;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Chat outbound client + pure formatting helpers.
|
|
3
|
+
*
|
|
4
|
+
* Outbound delivery uses Google Chat **incoming webhooks**: an HTTPS POST of a
|
|
5
|
+
* message resource to a per-space URL. The simplest body is `{ text }`; richer
|
|
6
|
+
* messages use Cards v2 (`cardsV2`). We keep the HTTP call behind a small
|
|
7
|
+
* `HttpFetch` seam so the formatters and routing can be unit-tested without the
|
|
8
|
+
* Paperclip SDK runtime.
|
|
9
|
+
*
|
|
10
|
+
* Docs: https://developers.google.com/workspace/chat/quickstart/webhooks
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* POST a message to a Google Chat space via its incoming-webhook URL.
|
|
14
|
+
* Returns the parsed result; throws on transport failure so callers can log.
|
|
15
|
+
*/
|
|
16
|
+
export async function postToWebhook(fetchImpl, webhookUrl, message) {
|
|
17
|
+
if (!webhookUrl)
|
|
18
|
+
throw new Error("postToWebhook: empty webhook URL");
|
|
19
|
+
const body = {};
|
|
20
|
+
if (message.text)
|
|
21
|
+
body.text = message.text;
|
|
22
|
+
if (message.cardsV2)
|
|
23
|
+
body.cardsV2 = message.cardsV2;
|
|
24
|
+
const url = message.threadKey
|
|
25
|
+
? appendQuery(webhookUrl, { threadKey: message.threadKey, messageReplyOption: "REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD" })
|
|
26
|
+
: webhookUrl;
|
|
27
|
+
const res = await fetchImpl(url, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "Content-Type": "application/json; charset=UTF-8" },
|
|
30
|
+
body: JSON.stringify(body),
|
|
31
|
+
});
|
|
32
|
+
const text = await res.text();
|
|
33
|
+
return { ok: res.ok, status: res.status, body: text };
|
|
34
|
+
}
|
|
35
|
+
/** Append query params without clobbering existing ones. Exported for tests. */
|
|
36
|
+
export function appendQuery(url, params) {
|
|
37
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
38
|
+
const qs = Object.entries(params)
|
|
39
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
|
40
|
+
.join("&");
|
|
41
|
+
return `${url}${sep}${qs}`;
|
|
42
|
+
}
|
|
43
|
+
// --- Pure formatters (unit-tested) -----------------------------------------
|
|
44
|
+
/** Google Chat supports a limited Markdown: *bold*, _italic_, `code`. */
|
|
45
|
+
export function bold(s) {
|
|
46
|
+
return `*${s}*`;
|
|
47
|
+
}
|
|
48
|
+
export function link(text, url) {
|
|
49
|
+
// Google Chat renders <url|text> as a hyperlink.
|
|
50
|
+
return `<${url}|${text}>`;
|
|
51
|
+
}
|
|
52
|
+
export function formatIssueCreated(issue) {
|
|
53
|
+
const title = issue.title ?? "(untitled)";
|
|
54
|
+
const head = issue.url ? link(`🆕 ${title}`, issue.url) : `🆕 ${title}`;
|
|
55
|
+
return `${head}\nNew issue created${issue.status ? ` · ${issue.status}` : ""}`;
|
|
56
|
+
}
|
|
57
|
+
export function formatIssueCompleted(issue) {
|
|
58
|
+
const title = issue.title ?? "(untitled)";
|
|
59
|
+
const head = issue.url ? link(`✅ ${title}`, issue.url) : `✅ ${title}`;
|
|
60
|
+
return `${head}\nIssue completed`;
|
|
61
|
+
}
|
|
62
|
+
export function formatApprovalRequested(issue) {
|
|
63
|
+
const title = issue.title ?? "(untitled)";
|
|
64
|
+
const head = issue.url ? link(`⏳ ${title}`, issue.url) : `⏳ ${title}`;
|
|
65
|
+
return `${bold("Approval needed")}\n${head}`;
|
|
66
|
+
}
|
|
67
|
+
export function formatAgentRunFailed(agentName, errorMsg) {
|
|
68
|
+
return `${bold("⚠️ Agent run failed")}\n${agentName}: ${errorMsg}`;
|
|
69
|
+
}
|
|
70
|
+
export function formatBriefing(title, body) {
|
|
71
|
+
return title ? `${bold(title)}\n${body}` : body;
|
|
72
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paperclip plugin manifest (apiVersion 1). Declares the capability grants the
|
|
3
|
+
* host enforces and the surfaces the worker registers. Keep capabilities minimal —
|
|
4
|
+
* the host blocks any API call whose capability is not declared here.
|
|
5
|
+
*/
|
|
6
|
+
declare const manifest: {
|
|
7
|
+
readonly id: "google-chat";
|
|
8
|
+
readonly apiVersion: 1;
|
|
9
|
+
readonly version: "0.1.0";
|
|
10
|
+
readonly displayName: "Google Chat";
|
|
11
|
+
readonly description: "Bidirectional Google Chat bridge: posts issue/agent/approval notifications to spaces and drives Paperclip from Chat slash commands.";
|
|
12
|
+
readonly author: "Measured Assets";
|
|
13
|
+
readonly categories: readonly ["connector", "automation"];
|
|
14
|
+
readonly capabilities: readonly ["companies.read", "projects.read", "issues.read", "issues.create", "issues.update", "issue.comments.read", "issue.comments.create", "agents.read", "agents.invoke", "agent.sessions.create", "agent.sessions.send", "goals.read", "events.subscribe", "jobs.schedule", "http.outbound", "secrets.read-ref", "webhooks.receive", "agent.tools.register", "activity.log.write", "plugin.state.read", "plugin.state.write", "instance.settings.register"];
|
|
15
|
+
readonly entrypoints: {
|
|
16
|
+
readonly worker: "./dist/worker.js";
|
|
17
|
+
};
|
|
18
|
+
readonly instanceConfigSchema: {
|
|
19
|
+
readonly type: "object";
|
|
20
|
+
readonly properties: {
|
|
21
|
+
readonly defaultWebhookUrlRef: {
|
|
22
|
+
readonly type: "string";
|
|
23
|
+
readonly title: "Default space webhook (secret ref)";
|
|
24
|
+
readonly description: "Secret reference to a Google Chat incoming-webhook URL. Create the secret in Paperclip, paste the returned UUID here.";
|
|
25
|
+
};
|
|
26
|
+
readonly approvalsWebhookUrlRef: {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
readonly title: "Approvals space webhook (secret ref)";
|
|
29
|
+
};
|
|
30
|
+
readonly errorsWebhookUrlRef: {
|
|
31
|
+
readonly type: "string";
|
|
32
|
+
readonly title: "Errors space webhook (secret ref)";
|
|
33
|
+
};
|
|
34
|
+
readonly digestWebhookUrlRef: {
|
|
35
|
+
readonly type: "string";
|
|
36
|
+
readonly title: "Digest space webhook (secret ref)";
|
|
37
|
+
};
|
|
38
|
+
readonly serviceAccountKeyRef: {
|
|
39
|
+
readonly type: "string";
|
|
40
|
+
readonly title: "Service-account key (secret ref, optional)";
|
|
41
|
+
readonly description: "Only required for the Chat REST API (threaded replies). Webhook-only mode leaves this blank.";
|
|
42
|
+
};
|
|
43
|
+
readonly notifyOnIssueCreated: {
|
|
44
|
+
readonly type: "boolean";
|
|
45
|
+
readonly title: "Notify on issue created";
|
|
46
|
+
readonly default: false;
|
|
47
|
+
};
|
|
48
|
+
readonly notifyOnIssueCompleted: {
|
|
49
|
+
readonly type: "boolean";
|
|
50
|
+
readonly title: "Notify on issue completed";
|
|
51
|
+
readonly default: true;
|
|
52
|
+
};
|
|
53
|
+
readonly notifyOnApprovalRequested: {
|
|
54
|
+
readonly type: "boolean";
|
|
55
|
+
readonly title: "Notify on approval requested";
|
|
56
|
+
readonly default: true;
|
|
57
|
+
};
|
|
58
|
+
readonly notifyOnAgentRunFailed: {
|
|
59
|
+
readonly type: "boolean";
|
|
60
|
+
readonly title: "Notify on agent run failed";
|
|
61
|
+
readonly default: true;
|
|
62
|
+
};
|
|
63
|
+
readonly useCards: {
|
|
64
|
+
readonly type: "boolean";
|
|
65
|
+
readonly title: "Use Cards v2 formatting";
|
|
66
|
+
readonly default: false;
|
|
67
|
+
};
|
|
68
|
+
readonly enableCommands: {
|
|
69
|
+
readonly type: "boolean";
|
|
70
|
+
readonly title: "Enable inbound slash commands";
|
|
71
|
+
readonly default: true;
|
|
72
|
+
};
|
|
73
|
+
readonly verificationTokenRef: {
|
|
74
|
+
readonly type: "string";
|
|
75
|
+
readonly title: "Verification token (secret ref)";
|
|
76
|
+
};
|
|
77
|
+
readonly allowedSpaceIds: {
|
|
78
|
+
readonly type: "array";
|
|
79
|
+
readonly title: "Allowed space IDs";
|
|
80
|
+
readonly items: {
|
|
81
|
+
readonly type: "string";
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
readonly allowedUserEmails: {
|
|
85
|
+
readonly type: "array";
|
|
86
|
+
readonly title: "Allowed user emails";
|
|
87
|
+
readonly items: {
|
|
88
|
+
readonly type: "string";
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
readonly digestMode: {
|
|
92
|
+
readonly type: "boolean";
|
|
93
|
+
readonly title: "Enable daily digest";
|
|
94
|
+
readonly default: false;
|
|
95
|
+
};
|
|
96
|
+
readonly dailyDigestTime: {
|
|
97
|
+
readonly type: "string";
|
|
98
|
+
readonly title: "Daily digest time (HH:MM)";
|
|
99
|
+
readonly default: "08:00";
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
readonly webhooks: readonly [{
|
|
104
|
+
readonly endpointKey: "chat-events";
|
|
105
|
+
readonly displayName: "Google Chat events";
|
|
106
|
+
readonly description: "Configure your Google Chat app's HTTP endpoint to POST events here (MESSAGE, ADDED_TO_SPACE, CARD_CLICKED).";
|
|
107
|
+
}];
|
|
108
|
+
readonly jobs: readonly [{
|
|
109
|
+
readonly key: "daily-digest";
|
|
110
|
+
readonly displayName: "Daily digest";
|
|
111
|
+
readonly description: "Posts a once-daily summary to the digest space when digestMode is enabled.";
|
|
112
|
+
readonly schedule: "0 * * * *";
|
|
113
|
+
}];
|
|
114
|
+
readonly tools: readonly [{
|
|
115
|
+
readonly name: "post_to_google_chat";
|
|
116
|
+
readonly displayName: "Post to Google Chat";
|
|
117
|
+
readonly description: "Post a plain-text or Markdown message to a configured Google Chat space.";
|
|
118
|
+
readonly parametersSchema: {
|
|
119
|
+
readonly type: "object";
|
|
120
|
+
readonly properties: {
|
|
121
|
+
readonly text: {
|
|
122
|
+
readonly type: "string";
|
|
123
|
+
readonly description: "Message body (Google Chat Markdown supported).";
|
|
124
|
+
};
|
|
125
|
+
readonly space: {
|
|
126
|
+
readonly type: "string";
|
|
127
|
+
readonly description: "Optional routing key: default | approvals | errors | digest. Defaults to 'default'.";
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
readonly required: readonly ["text"];
|
|
131
|
+
};
|
|
132
|
+
}, {
|
|
133
|
+
readonly name: "escalate_to_human";
|
|
134
|
+
readonly displayName: "Escalate to human (Google Chat)";
|
|
135
|
+
readonly description: "Post an escalation message to the approvals space and ask a human to respond.";
|
|
136
|
+
readonly parametersSchema: {
|
|
137
|
+
readonly type: "object";
|
|
138
|
+
readonly properties: {
|
|
139
|
+
readonly message: {
|
|
140
|
+
readonly type: "string";
|
|
141
|
+
};
|
|
142
|
+
readonly issueId: {
|
|
143
|
+
readonly type: "string";
|
|
144
|
+
readonly description: "Optional Paperclip issue id for context.";
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
readonly required: readonly ["message"];
|
|
148
|
+
};
|
|
149
|
+
}, {
|
|
150
|
+
readonly name: "send_briefing";
|
|
151
|
+
readonly displayName: "Send briefing (Google Chat)";
|
|
152
|
+
readonly description: "Post a formatted briefing/report to the digest space (used by scheduled briefing routines).";
|
|
153
|
+
readonly parametersSchema: {
|
|
154
|
+
readonly type: "object";
|
|
155
|
+
readonly properties: {
|
|
156
|
+
readonly title: {
|
|
157
|
+
readonly type: "string";
|
|
158
|
+
};
|
|
159
|
+
readonly body: {
|
|
160
|
+
readonly type: "string";
|
|
161
|
+
};
|
|
162
|
+
};
|
|
163
|
+
readonly required: readonly ["body"];
|
|
164
|
+
};
|
|
165
|
+
}];
|
|
166
|
+
};
|
|
167
|
+
export default manifest;
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { INSTANCE_CONFIG_SCHEMA } from "./config.js";
|
|
2
|
+
import { JOB_KEYS, PLUGIN_ID, PLUGIN_VERSION, TOOL_NAMES, WEBHOOK_KEYS } from "./constants.js";
|
|
3
|
+
/**
|
|
4
|
+
* Paperclip plugin manifest (apiVersion 1). Declares the capability grants the
|
|
5
|
+
* host enforces and the surfaces the worker registers. Keep capabilities minimal —
|
|
6
|
+
* the host blocks any API call whose capability is not declared here.
|
|
7
|
+
*/
|
|
8
|
+
const manifest = {
|
|
9
|
+
id: PLUGIN_ID,
|
|
10
|
+
apiVersion: 1,
|
|
11
|
+
version: PLUGIN_VERSION,
|
|
12
|
+
displayName: "Google Chat",
|
|
13
|
+
description: "Bidirectional Google Chat bridge: posts issue/agent/approval notifications to spaces and drives Paperclip from Chat slash commands.",
|
|
14
|
+
author: "Measured Assets",
|
|
15
|
+
categories: ["connector", "automation"],
|
|
16
|
+
capabilities: [
|
|
17
|
+
// read domain state for notifications + command responses
|
|
18
|
+
"companies.read",
|
|
19
|
+
"projects.read",
|
|
20
|
+
"issues.read",
|
|
21
|
+
"issues.create",
|
|
22
|
+
"issues.update",
|
|
23
|
+
"issue.comments.read",
|
|
24
|
+
"issue.comments.create",
|
|
25
|
+
"agents.read",
|
|
26
|
+
"agents.invoke",
|
|
27
|
+
"agent.sessions.create",
|
|
28
|
+
"agent.sessions.send",
|
|
29
|
+
"goals.read",
|
|
30
|
+
// outbound: event subscription + scheduled digest + HTTP to Google + secret refs
|
|
31
|
+
"events.subscribe",
|
|
32
|
+
"jobs.schedule",
|
|
33
|
+
"http.outbound",
|
|
34
|
+
"secrets.read-ref",
|
|
35
|
+
// inbound: receive Google Chat app events
|
|
36
|
+
"webhooks.receive",
|
|
37
|
+
// agents can proactively push to Chat
|
|
38
|
+
"agent.tools.register",
|
|
39
|
+
// misc
|
|
40
|
+
"activity.log.write",
|
|
41
|
+
"plugin.state.read",
|
|
42
|
+
"plugin.state.write",
|
|
43
|
+
"instance.settings.register",
|
|
44
|
+
],
|
|
45
|
+
entrypoints: {
|
|
46
|
+
worker: "./dist/worker.js",
|
|
47
|
+
},
|
|
48
|
+
instanceConfigSchema: INSTANCE_CONFIG_SCHEMA,
|
|
49
|
+
webhooks: [
|
|
50
|
+
{
|
|
51
|
+
endpointKey: WEBHOOK_KEYS.chatEvents,
|
|
52
|
+
displayName: "Google Chat events",
|
|
53
|
+
description: "Configure your Google Chat app's HTTP endpoint to POST events here (MESSAGE, ADDED_TO_SPACE, CARD_CLICKED).",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
jobs: [
|
|
57
|
+
{
|
|
58
|
+
key: JOB_KEYS.dailyDigest,
|
|
59
|
+
displayName: "Daily digest",
|
|
60
|
+
description: "Posts a once-daily summary to the digest space when digestMode is enabled.",
|
|
61
|
+
// Hourly tick; the handler self-gates to the configured HH:MM.
|
|
62
|
+
schedule: "0 * * * *",
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
tools: [
|
|
66
|
+
{
|
|
67
|
+
name: TOOL_NAMES.postMessage,
|
|
68
|
+
displayName: "Post to Google Chat",
|
|
69
|
+
description: "Post a plain-text or Markdown message to a configured Google Chat space.",
|
|
70
|
+
parametersSchema: {
|
|
71
|
+
type: "object",
|
|
72
|
+
properties: {
|
|
73
|
+
text: { type: "string", description: "Message body (Google Chat Markdown supported)." },
|
|
74
|
+
space: {
|
|
75
|
+
type: "string",
|
|
76
|
+
description: "Optional routing key: default | approvals | errors | digest. Defaults to 'default'.",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
required: ["text"],
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: TOOL_NAMES.escalateToHuman,
|
|
84
|
+
displayName: "Escalate to human (Google Chat)",
|
|
85
|
+
description: "Post an escalation message to the approvals space and ask a human to respond.",
|
|
86
|
+
parametersSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
properties: {
|
|
89
|
+
message: { type: "string" },
|
|
90
|
+
issueId: { type: "string", description: "Optional Paperclip issue id for context." },
|
|
91
|
+
},
|
|
92
|
+
required: ["message"],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: TOOL_NAMES.sendBriefing,
|
|
97
|
+
displayName: "Send briefing (Google Chat)",
|
|
98
|
+
description: "Post a formatted briefing/report to the digest space (used by scheduled briefing routines).",
|
|
99
|
+
parametersSchema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
title: { type: "string" },
|
|
103
|
+
body: { type: "string" },
|
|
104
|
+
},
|
|
105
|
+
required: ["body"],
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
export default manifest;
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-callable tools. Registered in the worker so agents can proactively push to
|
|
3
|
+
* Google Chat (e.g. a daily-briefing routine calling `send_briefing`, or an agent
|
|
4
|
+
* escalating to a human). Each handler returns a `ToolResult` ({ content | error }).
|
|
5
|
+
*/
|
|
6
|
+
import { type GoogleChatMessage, type RouteKey } from "./google-chat.js";
|
|
7
|
+
/** Posts a message to a routed space; returns delivery status. */
|
|
8
|
+
export type PostFn = (routeKey: RouteKey, message: GoogleChatMessage) => Promise<{
|
|
9
|
+
ok: boolean;
|
|
10
|
+
status: number;
|
|
11
|
+
body: string;
|
|
12
|
+
}>;
|
|
13
|
+
interface ToolResultLike {
|
|
14
|
+
content?: string;
|
|
15
|
+
data?: unknown;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
interface ToolsHost {
|
|
19
|
+
tools: {
|
|
20
|
+
register(name: string, declaration: {
|
|
21
|
+
displayName: string;
|
|
22
|
+
description: string;
|
|
23
|
+
parametersSchema: unknown;
|
|
24
|
+
}, fn: (params: unknown, runCtx: unknown) => Promise<ToolResultLike>): void;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export declare function registerTools(ctx: ToolsHost, post: PostFn): void;
|
|
28
|
+
export {};
|
package/dist/tools.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-callable tools. Registered in the worker so agents can proactively push to
|
|
3
|
+
* Google Chat (e.g. a daily-briefing routine calling `send_briefing`, or an agent
|
|
4
|
+
* escalating to a human). Each handler returns a `ToolResult` ({ content | error }).
|
|
5
|
+
*/
|
|
6
|
+
import { TOOL_NAMES } from "./constants.js";
|
|
7
|
+
import { formatBriefing } from "./google-chat.js";
|
|
8
|
+
export function registerTools(ctx, post) {
|
|
9
|
+
ctx.tools.register(TOOL_NAMES.postMessage, {
|
|
10
|
+
displayName: "Post to Google Chat",
|
|
11
|
+
description: "Post a plain-text or Markdown message to a configured Google Chat space.",
|
|
12
|
+
parametersSchema: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
text: { type: "string" },
|
|
16
|
+
space: { type: "string", description: "default | approvals | errors | digest" },
|
|
17
|
+
},
|
|
18
|
+
required: ["text"],
|
|
19
|
+
},
|
|
20
|
+
}, async (params) => {
|
|
21
|
+
const p = (params ?? {});
|
|
22
|
+
if (!p.text)
|
|
23
|
+
return { error: "text is required" };
|
|
24
|
+
const route = normalizeRoute(p.space);
|
|
25
|
+
const res = await post(route, { text: p.text });
|
|
26
|
+
return res.ok
|
|
27
|
+
? { content: `Posted to the ${route} space.` }
|
|
28
|
+
: { error: `Google Chat post failed (HTTP ${res.status}): ${res.body.slice(0, 200)}` };
|
|
29
|
+
});
|
|
30
|
+
ctx.tools.register(TOOL_NAMES.escalateToHuman, {
|
|
31
|
+
displayName: "Escalate to human (Google Chat)",
|
|
32
|
+
description: "Post an escalation to the approvals space and ask a human to respond.",
|
|
33
|
+
parametersSchema: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: { message: { type: "string" }, issueId: { type: "string" } },
|
|
36
|
+
required: ["message"],
|
|
37
|
+
},
|
|
38
|
+
}, async (params) => {
|
|
39
|
+
const p = (params ?? {});
|
|
40
|
+
if (!p.message)
|
|
41
|
+
return { error: "message is required" };
|
|
42
|
+
const suffix = p.issueId ? `\n_issue: ${p.issueId}_` : "";
|
|
43
|
+
const res = await post("approvals", { text: `🙋 *Escalation*\n${p.message}${suffix}` });
|
|
44
|
+
return res.ok ? { content: "Escalation posted to the approvals space." } : { error: `Post failed (HTTP ${res.status}).` };
|
|
45
|
+
});
|
|
46
|
+
ctx.tools.register(TOOL_NAMES.sendBriefing, {
|
|
47
|
+
displayName: "Send briefing (Google Chat)",
|
|
48
|
+
description: "Post a formatted briefing/report to the digest space.",
|
|
49
|
+
parametersSchema: {
|
|
50
|
+
type: "object",
|
|
51
|
+
properties: { title: { type: "string" }, body: { type: "string" } },
|
|
52
|
+
required: ["body"],
|
|
53
|
+
},
|
|
54
|
+
}, async (params) => {
|
|
55
|
+
const p = (params ?? {});
|
|
56
|
+
if (!p.body)
|
|
57
|
+
return { error: "body is required" };
|
|
58
|
+
const res = await post("digest", { text: formatBriefing(p.title, p.body) });
|
|
59
|
+
return res.ok ? { content: "Briefing posted to the digest space." } : { error: `Post failed (HTTP ${res.status}).` };
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function normalizeRoute(space) {
|
|
63
|
+
switch (space) {
|
|
64
|
+
case "approvals":
|
|
65
|
+
case "errors":
|
|
66
|
+
case "digest":
|
|
67
|
+
return space;
|
|
68
|
+
default:
|
|
69
|
+
return "default";
|
|
70
|
+
}
|
|
71
|
+
}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Chat plugin worker — wires the SDK seams together:
|
|
3
|
+
*
|
|
4
|
+
* - setup() registers domain-event subscriptions, the daily-digest job, and
|
|
5
|
+
* the agent tools.
|
|
6
|
+
* - onWebhook() receives Google Chat app events and routes slash commands.
|
|
7
|
+
* - outbound resolves a per-route incoming-webhook URL from a secret ref and
|
|
8
|
+
* POSTs the message via `ctx.http.fetch`.
|
|
9
|
+
*
|
|
10
|
+
* The pure logic (formatting, parsing, routing) lives in sibling modules and is
|
|
11
|
+
* unit-tested; this file is the thin SDK-facing shell.
|
|
12
|
+
*/
|
|
13
|
+
declare const plugin: import("@paperclipai/plugin-sdk").PaperclipPlugin;
|
|
14
|
+
export default plugin;
|