openryoko 2026.5.8 → 2026.5.13
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/dist/package.json +1 -1
- package/dist/src/connectors/slack/__tests__/agents-canvas.test.d.ts +2 -0
- package/dist/src/connectors/slack/__tests__/agents-canvas.test.d.ts.map +1 -0
- package/dist/src/connectors/slack/__tests__/agents-canvas.test.js +191 -0
- package/dist/src/connectors/slack/__tests__/agents-canvas.test.js.map +1 -0
- package/dist/src/connectors/slack/__tests__/goal-extractor.test.d.ts +2 -0
- package/dist/src/connectors/slack/__tests__/goal-extractor.test.d.ts.map +1 -0
- package/dist/src/connectors/slack/__tests__/goal-extractor.test.js +129 -0
- package/dist/src/connectors/slack/__tests__/goal-extractor.test.js.map +1 -0
- package/dist/src/connectors/slack/agents-canvas.d.ts +59 -0
- package/dist/src/connectors/slack/agents-canvas.d.ts.map +1 -0
- package/dist/src/connectors/slack/agents-canvas.js +372 -0
- package/dist/src/connectors/slack/agents-canvas.js.map +1 -0
- package/dist/src/connectors/slack/goal-extractor.d.ts +67 -0
- package/dist/src/connectors/slack/goal-extractor.d.ts.map +1 -0
- package/dist/src/connectors/slack/goal-extractor.js +302 -0
- package/dist/src/connectors/slack/goal-extractor.js.map +1 -0
- package/dist/src/connectors/slack/index.d.ts +15 -0
- package/dist/src/connectors/slack/index.d.ts.map +1 -1
- package/dist/src/connectors/slack/index.js +78 -0
- package/dist/src/connectors/slack/index.js.map +1 -1
- package/dist/src/engines/claude.d.ts +11 -0
- package/dist/src/engines/claude.d.ts.map +1 -1
- package/dist/src/engines/claude.js +75 -13
- package/dist/src/engines/claude.js.map +1 -1
- package/dist/src/gateway/api.d.ts.map +1 -1
- package/dist/src/gateway/api.js +20 -0
- package/dist/src/gateway/api.js.map +1 -1
- package/dist/src/gateway/server.d.ts.map +1 -1
- package/dist/src/gateway/server.js +61 -2
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/sessions/context.js +20 -2
- package/dist/src/sessions/context.js.map +1 -1
- package/dist/src/sessions/manager.d.ts.map +1 -1
- package/dist/src/sessions/manager.js +15 -1
- package/dist/src/sessions/manager.js.map +1 -1
- package/dist/src/shared/types.d.ts +21 -0
- package/dist/src/shared/types.d.ts.map +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/__next.__PAGE__.txt +2 -2
- package/dist/web/__next._full.txt +3 -3
- package/dist/web/__next._head.txt +1 -1
- package/dist/web/__next._index.txt +2 -2
- package/dist/web/__next._tree.txt +2 -2
- package/dist/web/_next/static/chunks/{0y3uv64m1qh.k.js → 012aakl34ff5a.js} +2 -2
- package/dist/web/_next/static/chunks/{0j4uz-g9x1_~s.js → 0a2uy1kw.~j~c.js} +1 -1
- package/dist/web/_next/static/chunks/{0v51a3blij~j6.js → 0agxgztb0b~.7.js} +2 -2
- package/dist/web/_next/static/chunks/{1645~4k0qcst0.js → 0g6xt.91k~0bc.js} +2 -2
- package/dist/web/_next/static/chunks/{16zc7-yv7supr.js → 0jzw1gpuag8bc.js} +2 -2
- package/dist/web/_next/static/chunks/0kceasmxfr3s1.js +1 -0
- package/dist/web/_next/static/chunks/{0wv0qjh9tuqx~.js → 0lkp92-ovteta.js} +2 -2
- package/dist/web/_next/static/chunks/{1621s_jgljr1_.js → 0msmlzd_~uk0y.js} +2 -2
- package/dist/web/_next/static/chunks/{00yn.jp4tr9l6.js → 0z5lzlvft9.4o.js} +2 -2
- package/dist/web/_next/static/chunks/103zsy0q9psfj.css +1 -0
- package/dist/web/_next/static/chunks/{0ma0h4_2c8y9y.js → 12m97bwrg1tuu.js} +2 -2
- package/dist/web/_not-found/__next._full.txt +2 -2
- package/dist/web/_not-found/__next._head.txt +1 -1
- package/dist/web/_not-found/__next._index.txt +2 -2
- package/dist/web/_not-found/__next._not-found.__PAGE__.txt +1 -1
- package/dist/web/_not-found/__next._not-found.txt +1 -1
- package/dist/web/_not-found/__next._tree.txt +2 -2
- package/dist/web/_not-found.html +1 -1
- package/dist/web/_not-found.txt +2 -2
- package/dist/web/chat/__next._full.txt +3 -3
- package/dist/web/chat/__next._head.txt +1 -1
- package/dist/web/chat/__next._index.txt +2 -2
- package/dist/web/chat/__next._tree.txt +2 -2
- package/dist/web/chat/__next.chat.__PAGE__.txt +2 -2
- package/dist/web/chat/__next.chat.txt +1 -1
- package/dist/web/chat.html +1 -1
- package/dist/web/chat.txt +3 -3
- package/dist/web/cron/__next._full.txt +3 -3
- package/dist/web/cron/__next._head.txt +1 -1
- package/dist/web/cron/__next._index.txt +2 -2
- package/dist/web/cron/__next._tree.txt +2 -2
- package/dist/web/cron/__next.cron.__PAGE__.txt +2 -2
- package/dist/web/cron/__next.cron.txt +1 -1
- package/dist/web/cron.html +1 -1
- package/dist/web/cron.txt +3 -3
- package/dist/web/index.html +1 -1
- package/dist/web/index.txt +3 -3
- package/dist/web/kanban/__next._full.txt +3 -3
- package/dist/web/kanban/__next._head.txt +1 -1
- package/dist/web/kanban/__next._index.txt +2 -2
- package/dist/web/kanban/__next._tree.txt +2 -2
- package/dist/web/kanban/__next.kanban.__PAGE__.txt +2 -2
- package/dist/web/kanban/__next.kanban.txt +1 -1
- package/dist/web/kanban.html +1 -1
- package/dist/web/kanban.txt +3 -3
- package/dist/web/logs/__next._full.txt +3 -3
- package/dist/web/logs/__next._head.txt +1 -1
- package/dist/web/logs/__next._index.txt +2 -2
- package/dist/web/logs/__next._tree.txt +2 -2
- package/dist/web/logs/__next.logs.__PAGE__.txt +2 -2
- package/dist/web/logs/__next.logs.txt +1 -1
- package/dist/web/logs.html +2 -2
- package/dist/web/logs.txt +3 -3
- package/dist/web/org/__next._full.txt +3 -3
- package/dist/web/org/__next._head.txt +1 -1
- package/dist/web/org/__next._index.txt +2 -2
- package/dist/web/org/__next._tree.txt +2 -2
- package/dist/web/org/__next.org.__PAGE__.txt +2 -2
- package/dist/web/org/__next.org.txt +1 -1
- package/dist/web/org.html +1 -1
- package/dist/web/org.txt +3 -3
- package/dist/web/sessions/__next._full.txt +2 -2
- package/dist/web/sessions/__next._head.txt +1 -1
- package/dist/web/sessions/__next._index.txt +2 -2
- package/dist/web/sessions/__next._tree.txt +2 -2
- package/dist/web/sessions/__next.sessions.__PAGE__.txt +1 -1
- package/dist/web/sessions/__next.sessions.txt +1 -1
- package/dist/web/sessions.html +1 -1
- package/dist/web/sessions.txt +2 -2
- package/dist/web/settings/__next._full.txt +3 -3
- package/dist/web/settings/__next._head.txt +1 -1
- package/dist/web/settings/__next._index.txt +2 -2
- package/dist/web/settings/__next._tree.txt +2 -2
- package/dist/web/settings/__next.settings.__PAGE__.txt +2 -2
- package/dist/web/settings/__next.settings.txt +1 -1
- package/dist/web/settings.html +1 -1
- package/dist/web/settings.txt +3 -3
- package/dist/web/skills/__next._full.txt +3 -3
- package/dist/web/skills/__next._head.txt +1 -1
- package/dist/web/skills/__next._index.txt +2 -2
- package/dist/web/skills/__next._tree.txt +2 -2
- package/dist/web/skills/__next.skills.__PAGE__.txt +2 -2
- package/dist/web/skills/__next.skills.txt +1 -1
- package/dist/web/skills.html +1 -1
- package/dist/web/skills.txt +3 -3
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/05uy~i7wsbsn3.js +0 -1
- package/dist/web/_next/static/chunks/0bj62gv2pext..css +0 -1
- package/dist/web/dev/logs/next-development.log +0 -5
- /package/dist/web/_next/static/{VBjMXtz9-pIo9izqNT-iu → 2gkySyM5UoStZGUIrVpRy}/_buildManifest.js +0 -0
- /package/dist/web/_next/static/{VBjMXtz9-pIo9izqNT-iu → 2gkySyM5UoStZGUIrVpRy}/_clientMiddlewareManifest.js +0 -0
- /package/dist/web/_next/static/{VBjMXtz9-pIo9izqNT-iu → 2gkySyM5UoStZGUIrVpRy}/_ssgManifest.js +0 -0
package/dist/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents-canvas.test.d.ts","sourceRoot":"","sources":["../../../../../src/connectors/slack/__tests__/agents-canvas.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { renderCanvasMarkdown } from "../agents-canvas.js";
|
|
3
|
+
function makeSession(overrides = {}) {
|
|
4
|
+
return {
|
|
5
|
+
id: "sess-1",
|
|
6
|
+
engine: "claude",
|
|
7
|
+
engineSessionId: "claude-abc",
|
|
8
|
+
source: "slack",
|
|
9
|
+
sourceRef: "C123:1700000000.000",
|
|
10
|
+
connector: "slack",
|
|
11
|
+
sessionKey: "slack:C123",
|
|
12
|
+
replyContext: null,
|
|
13
|
+
messageId: "1700000000.000",
|
|
14
|
+
transportMeta: null,
|
|
15
|
+
employee: null,
|
|
16
|
+
model: null,
|
|
17
|
+
title: "Untitled",
|
|
18
|
+
parentSessionId: null,
|
|
19
|
+
status: "idle",
|
|
20
|
+
effortLevel: null,
|
|
21
|
+
totalCost: 0,
|
|
22
|
+
totalTurns: 0,
|
|
23
|
+
createdAt: "2026-05-13T01:00:00.000Z",
|
|
24
|
+
lastActivity: "2026-05-13T01:00:00.000Z",
|
|
25
|
+
lastError: null,
|
|
26
|
+
...overrides,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const FIXED_NOW = Date.parse("2026-05-13T01:30:00.000Z");
|
|
30
|
+
describe("renderCanvasMarkdown", () => {
|
|
31
|
+
it("renders a placeholder when there are no sessions", () => {
|
|
32
|
+
const md = renderCanvasMarkdown([], { nowMs: FIXED_NOW });
|
|
33
|
+
expect(md).toContain("# Ryoko Agents View");
|
|
34
|
+
expect(md).toContain("0 sessions");
|
|
35
|
+
expect(md).toContain("No active sessions");
|
|
36
|
+
});
|
|
37
|
+
it("groups sessions by status", () => {
|
|
38
|
+
const sessions = [
|
|
39
|
+
makeSession({ id: "a", status: "running", title: "Build proposal" }),
|
|
40
|
+
makeSession({ id: "b", status: "waiting", title: "Awaiting reply" }),
|
|
41
|
+
makeSession({ id: "c", status: "error", title: "Broken thing", lastError: "boom" }),
|
|
42
|
+
makeSession({ id: "d", status: "interrupted", title: "Old run" }),
|
|
43
|
+
makeSession({ id: "e", status: "idle", title: "Wrapped up" }),
|
|
44
|
+
];
|
|
45
|
+
const md = renderCanvasMarkdown(sessions, { nowMs: FIXED_NOW });
|
|
46
|
+
expect(md).toContain("🟢 Running (1)");
|
|
47
|
+
expect(md).toContain("🟡 Waiting on you (1)");
|
|
48
|
+
expect(md).toContain("🔴 Errored (1)");
|
|
49
|
+
expect(md).toContain("⏸️ Interrupted (resumable) (1)");
|
|
50
|
+
expect(md).toContain("✅ Recently idle (1)");
|
|
51
|
+
expect(md).toContain("**Build proposal**");
|
|
52
|
+
expect(md).toContain("**Awaiting reply**");
|
|
53
|
+
});
|
|
54
|
+
it("omits empty groups", () => {
|
|
55
|
+
const sessions = [makeSession({ status: "running", title: "Just one" })];
|
|
56
|
+
const md = renderCanvasMarkdown(sessions, { nowMs: FIXED_NOW });
|
|
57
|
+
expect(md).toContain("🟢 Running (1)");
|
|
58
|
+
expect(md).not.toContain("Waiting on you");
|
|
59
|
+
expect(md).not.toContain("Errored");
|
|
60
|
+
expect(md).not.toContain("Recently idle");
|
|
61
|
+
});
|
|
62
|
+
it("caps each group at maxPerGroup and shows overflow line", () => {
|
|
63
|
+
const sessions = Array.from({ length: 15 }, (_, i) => makeSession({ id: `r${i}`, status: "running", title: `task ${i}` }));
|
|
64
|
+
const md = renderCanvasMarkdown(sessions, { nowMs: FIXED_NOW, maxPerGroup: 3 });
|
|
65
|
+
expect(md).toContain("🟢 Running (15)");
|
|
66
|
+
expect(md).toContain("**task 0**");
|
|
67
|
+
expect(md).toContain("**task 2**");
|
|
68
|
+
expect(md).not.toContain("**task 3**");
|
|
69
|
+
expect(md).toContain("…and 12 more");
|
|
70
|
+
});
|
|
71
|
+
it("formats age in minutes / hours / days", () => {
|
|
72
|
+
const sessions = [
|
|
73
|
+
makeSession({
|
|
74
|
+
id: "fresh",
|
|
75
|
+
status: "running",
|
|
76
|
+
title: "fresh",
|
|
77
|
+
lastActivity: new Date(FIXED_NOW - 2 * 60_000).toISOString(),
|
|
78
|
+
}),
|
|
79
|
+
makeSession({
|
|
80
|
+
id: "old",
|
|
81
|
+
status: "running",
|
|
82
|
+
title: "hours-old",
|
|
83
|
+
lastActivity: new Date(FIXED_NOW - 3 * 60 * 60_000).toISOString(),
|
|
84
|
+
}),
|
|
85
|
+
makeSession({
|
|
86
|
+
id: "ancient",
|
|
87
|
+
status: "running",
|
|
88
|
+
title: "days-old",
|
|
89
|
+
lastActivity: new Date(FIXED_NOW - 5 * 24 * 60 * 60_000).toISOString(),
|
|
90
|
+
}),
|
|
91
|
+
];
|
|
92
|
+
const md = renderCanvasMarkdown(sessions, { nowMs: FIXED_NOW });
|
|
93
|
+
expect(md).toContain("**fresh**");
|
|
94
|
+
expect(md).toMatch(/\*\*fresh\*\*.* — 2m/);
|
|
95
|
+
expect(md).toMatch(/\*\*hours-old\*\*.* — 3h/);
|
|
96
|
+
expect(md).toMatch(/\*\*days-old\*\*.* — 5d/);
|
|
97
|
+
});
|
|
98
|
+
it("truncates very long titles", () => {
|
|
99
|
+
const longTitle = "x".repeat(200);
|
|
100
|
+
const md = renderCanvasMarkdown([makeSession({ title: longTitle, status: "running" })], { nowMs: FIXED_NOW });
|
|
101
|
+
expect(md).toContain("…");
|
|
102
|
+
// Should not include the full 200 chars
|
|
103
|
+
expect(md).not.toContain(longTitle);
|
|
104
|
+
});
|
|
105
|
+
it("honors a custom title", () => {
|
|
106
|
+
const md = renderCanvasMarkdown([], { title: "Team Ryoko", nowMs: FIXED_NOW });
|
|
107
|
+
expect(md).toContain("# Team Ryoko");
|
|
108
|
+
});
|
|
109
|
+
it("includes connector/employee in the line metadata when present", () => {
|
|
110
|
+
const md = renderCanvasMarkdown([
|
|
111
|
+
makeSession({
|
|
112
|
+
status: "running",
|
|
113
|
+
title: "with-meta",
|
|
114
|
+
connector: "slack",
|
|
115
|
+
source: "slack",
|
|
116
|
+
employee: "ryoko",
|
|
117
|
+
}),
|
|
118
|
+
], { nowMs: FIXED_NOW });
|
|
119
|
+
expect(md).toContain("**with-meta** _(slack · @ryoko)_");
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
describe("renderCanvasMarkdown — user-controlled string defang", () => {
|
|
123
|
+
// Regression coverage for security review M1: a malicious session title
|
|
124
|
+
// (or connector / employee field) must not be able to ping anyone or
|
|
125
|
+
// hijack the canvas Markdown structure.
|
|
126
|
+
it("strips Slack <@U…> user mentions", () => {
|
|
127
|
+
const md = renderCanvasMarkdown([makeSession({ status: "running", title: "ping <@U0123456> hi" })], { nowMs: FIXED_NOW });
|
|
128
|
+
expect(md).toContain("(user)");
|
|
129
|
+
expect(md).not.toMatch(/<@U\d/);
|
|
130
|
+
});
|
|
131
|
+
it("strips Slack <@U…|alias> aliased user mentions but keeps the label", () => {
|
|
132
|
+
const md = renderCanvasMarkdown([makeSession({ status: "running", title: "by <@U0123456|alice> done" })], { nowMs: FIXED_NOW });
|
|
133
|
+
expect(md).toContain("alice");
|
|
134
|
+
expect(md).not.toContain("<@U");
|
|
135
|
+
});
|
|
136
|
+
it("strips Slack <#C…> channel links", () => {
|
|
137
|
+
const md = renderCanvasMarkdown([makeSession({ status: "running", title: "see <#C09U11F6KEG>" })], { nowMs: FIXED_NOW });
|
|
138
|
+
expect(md).toContain("(channel)");
|
|
139
|
+
expect(md).not.toContain("<#C");
|
|
140
|
+
});
|
|
141
|
+
it("rewrites <#C…|name> channel links to #name", () => {
|
|
142
|
+
const md = renderCanvasMarkdown([makeSession({ status: "running", title: "from <#C123|agent-ops>" })], { nowMs: FIXED_NOW });
|
|
143
|
+
expect(md).toContain("#agent-ops");
|
|
144
|
+
expect(md).not.toContain("<#C");
|
|
145
|
+
});
|
|
146
|
+
it("strips Slack <!channel>, <!here>, <!everyone> broadcast mentions", () => {
|
|
147
|
+
const md = renderCanvasMarkdown([
|
|
148
|
+
makeSession({ id: "a", status: "running", title: "<!channel> heads up" }),
|
|
149
|
+
makeSession({ id: "b", status: "running", title: "<!here> attention" }),
|
|
150
|
+
makeSession({ id: "c", status: "running", title: "<!everyone> all hands" }),
|
|
151
|
+
], { nowMs: FIXED_NOW });
|
|
152
|
+
expect(md).toContain("(channel)");
|
|
153
|
+
expect(md).toContain("(here)");
|
|
154
|
+
expect(md).toContain("(everyone)");
|
|
155
|
+
expect(md).not.toMatch(/<![a-z]+>/);
|
|
156
|
+
});
|
|
157
|
+
it("inserts a zero-width space inside bare @channel / @here / @everyone", () => {
|
|
158
|
+
const md = renderCanvasMarkdown([makeSession({ status: "running", title: "hey @channel team" })], { nowMs: FIXED_NOW });
|
|
159
|
+
// zero-width space (U+200B) between @ and the keyword breaks the ping
|
|
160
|
+
expect(md).toMatch(/@channel/);
|
|
161
|
+
});
|
|
162
|
+
it("strips Slack subteam mentions (<!subteam^S…>)", () => {
|
|
163
|
+
const md = renderCanvasMarkdown([makeSession({ status: "running", title: "for <!subteam^SAZ94GDB8>" })], { nowMs: FIXED_NOW });
|
|
164
|
+
expect(md).toContain("(group)");
|
|
165
|
+
expect(md).not.toContain("<!subteam");
|
|
166
|
+
});
|
|
167
|
+
it("escapes Markdown emphasis / heading / blockquote / table / strike chars", () => {
|
|
168
|
+
const md = renderCanvasMarkdown([
|
|
169
|
+
makeSession({
|
|
170
|
+
status: "running",
|
|
171
|
+
title: "# big *bold* `code` ~~strike~~ > quote | col [link]",
|
|
172
|
+
}),
|
|
173
|
+
], { nowMs: FIXED_NOW });
|
|
174
|
+
expect(md).toContain("\\#");
|
|
175
|
+
expect(md).toContain("\\*");
|
|
176
|
+
expect(md).toContain("\\`");
|
|
177
|
+
expect(md).toContain("\\~");
|
|
178
|
+
expect(md).toContain("\\>");
|
|
179
|
+
expect(md).toContain("\\|");
|
|
180
|
+
expect(md).toContain("\\[");
|
|
181
|
+
expect(md).toContain("\\]");
|
|
182
|
+
});
|
|
183
|
+
it("collapses embedded newlines so list items don't break", () => {
|
|
184
|
+
const md = renderCanvasMarkdown([makeSession({ status: "running", title: "line1\nline2\n# inject" })], { nowMs: FIXED_NOW });
|
|
185
|
+
// The title should land on one logical line (no Markdown line break);
|
|
186
|
+
// every Markdown control character is escaped.
|
|
187
|
+
expect(md.split("\n").filter((l) => l.includes("line1")).length).toBe(1);
|
|
188
|
+
expect(md).toContain("\\#");
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
//# sourceMappingURL=agents-canvas.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents-canvas.test.js","sourceRoot":"","sources":["../../../../../src/connectors/slack/__tests__/agents-canvas.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-extractor.test.d.ts","sourceRoot":"","sources":["../../../../../src/connectors/slack/__tests__/goal-extractor.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { shouldExtractGoal, hasGoalIntent, buildGoalExtractionPrompt, parseGoalExtractionResult, } from "../goal-extractor.js";
|
|
3
|
+
describe("shouldExtractGoal — accepts substantive messages", () => {
|
|
4
|
+
it.each([
|
|
5
|
+
// Substantive task requests (Haiku will decide if they need /goal)
|
|
6
|
+
"OpenRyokoのGithub調査して紹介資料作成まで「最後までやって」",
|
|
7
|
+
"5社のSaaSを比較した表をスレッドに投げて",
|
|
8
|
+
"数字1から3までを別々のターンで出して完了と書いたら止まる",
|
|
9
|
+
"ブログ記事のドラフトを書いて",
|
|
10
|
+
"Just tell me about Tailscale",
|
|
11
|
+
])("accepts %s", (text) => {
|
|
12
|
+
expect(shouldExtractGoal(text)).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
describe("shouldExtractGoal — skips trivial / pre-prefixed messages", () => {
|
|
16
|
+
it("skips empty", () => {
|
|
17
|
+
expect(shouldExtractGoal("")).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
it("skips whitespace-only", () => {
|
|
20
|
+
expect(shouldExtractGoal(" \n\t ")).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
it("skips very short acknowledgements", () => {
|
|
23
|
+
expect(shouldExtractGoal("ok")).toBe(false);
|
|
24
|
+
expect(shouldExtractGoal("thanks")).toBe(false);
|
|
25
|
+
expect(shouldExtractGoal("はい")).toBe(false);
|
|
26
|
+
expect(shouldExtractGoal("👍")).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
it("skips messages already prefixed with /goal", () => {
|
|
29
|
+
expect(shouldExtractGoal("/goal Posted the comparison\n\nfollow up")).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe("hasGoalIntent (deprecated alias)", () => {
|
|
33
|
+
it("forwards to shouldExtractGoal", () => {
|
|
34
|
+
expect(hasGoalIntent("a substantive request")).toBe(true);
|
|
35
|
+
expect(hasGoalIntent("ok")).toBe(false);
|
|
36
|
+
expect(hasGoalIntent("")).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe("buildGoalExtractionPrompt", () => {
|
|
40
|
+
it("embeds the user message verbatim", () => {
|
|
41
|
+
const p = buildGoalExtractionPrompt("テスト最後までやってね");
|
|
42
|
+
expect(p).toContain("テスト最後までやってね");
|
|
43
|
+
});
|
|
44
|
+
it("instructs JSON output with condition key", () => {
|
|
45
|
+
const p = buildGoalExtractionPrompt("anything");
|
|
46
|
+
expect(p).toContain('"condition"');
|
|
47
|
+
expect(p).toContain("JSON only");
|
|
48
|
+
});
|
|
49
|
+
it("biases the LLM toward null on ambiguity", () => {
|
|
50
|
+
const p = buildGoalExtractionPrompt("anything");
|
|
51
|
+
expect(p).toContain('{"condition": null}');
|
|
52
|
+
expect(p).toContain("be conservative");
|
|
53
|
+
expect(p).toMatch(/when in doubt[, ]+null/i);
|
|
54
|
+
});
|
|
55
|
+
it("documents the four trigger families (keep-going, notify, embedded stop, pipeline)", () => {
|
|
56
|
+
const p = buildGoalExtractionPrompt("anything");
|
|
57
|
+
expect(p).toContain("完成するまで止まらないで");
|
|
58
|
+
expect(p).toContain("終わったらSlackで教えて");
|
|
59
|
+
expect(p).toContain("完了と書いたら止まる");
|
|
60
|
+
expect(p).toContain("AUTONOMOUSLY ACROSS MANY TURNS");
|
|
61
|
+
});
|
|
62
|
+
it("truncates very long messages", () => {
|
|
63
|
+
const huge = "x".repeat(5000);
|
|
64
|
+
const p = buildGoalExtractionPrompt(huge);
|
|
65
|
+
expect(p).toContain("…(truncated)");
|
|
66
|
+
expect(p.length).toBeLessThan(huge.length + 4000);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
describe("parseGoalExtractionResult — happy path", () => {
|
|
70
|
+
it("parses a plain JSON object", () => {
|
|
71
|
+
const out = parseGoalExtractionResult('{"condition":"Posted a comparison table to the thread"}');
|
|
72
|
+
expect(out).toBe("Posted a comparison table to the thread");
|
|
73
|
+
});
|
|
74
|
+
it("parses inside a fenced ```json``` block", () => {
|
|
75
|
+
const out = parseGoalExtractionResult('```json\n{"condition":"Closed every open Linear issue under project A"}\n```');
|
|
76
|
+
expect(out).toBe("Closed every open Linear issue under project A");
|
|
77
|
+
});
|
|
78
|
+
it("accepts JSON wrapped in surrounding prose", () => {
|
|
79
|
+
const out = parseGoalExtractionResult('Sure, here it is:\n\n{"condition":"All five SaaS rows added"}\n\nDone.');
|
|
80
|
+
expect(out).toBe("All five SaaS rows added");
|
|
81
|
+
});
|
|
82
|
+
it("flattens whitespace and trims", () => {
|
|
83
|
+
const out = parseGoalExtractionResult('{"condition":" Posted the\\n table\\nto the thread. "}');
|
|
84
|
+
expect(out).toBe("Posted the table to the thread.");
|
|
85
|
+
});
|
|
86
|
+
it("strips control characters", () => {
|
|
87
|
+
const out = parseGoalExtractionResult(JSON.stringify({ condition: "Done with task X" }));
|
|
88
|
+
expect(out).toBe("Done with task X");
|
|
89
|
+
});
|
|
90
|
+
it("hard caps at 400 chars", () => {
|
|
91
|
+
const long = "x".repeat(800);
|
|
92
|
+
const out = parseGoalExtractionResult(JSON.stringify({ condition: long }));
|
|
93
|
+
expect(out.length).toBeLessThanOrEqual(400);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe("parseGoalExtractionResult — null / refusals", () => {
|
|
97
|
+
it("returns null for explicit null condition", () => {
|
|
98
|
+
expect(parseGoalExtractionResult('{"condition":null}')).toBeNull();
|
|
99
|
+
});
|
|
100
|
+
it("returns null for empty string condition", () => {
|
|
101
|
+
expect(parseGoalExtractionResult('{"condition":""}')).toBeNull();
|
|
102
|
+
});
|
|
103
|
+
it("returns null for whitespace-only condition", () => {
|
|
104
|
+
expect(parseGoalExtractionResult('{"condition":" \\n\\t "}')).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
it("returns null for unparseable output", () => {
|
|
107
|
+
expect(parseGoalExtractionResult("not json")).toBeNull();
|
|
108
|
+
expect(parseGoalExtractionResult("")).toBeNull();
|
|
109
|
+
expect(parseGoalExtractionResult("{}")).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
it("returns null for sentinel placeholders", () => {
|
|
112
|
+
for (const v of ["none", "N/A", "null", "unknown", "tbd"]) {
|
|
113
|
+
expect(parseGoalExtractionResult(JSON.stringify({ condition: v }))).toBeNull();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
it("returns null for human-judgement conditions starting with 'The user is'", () => {
|
|
117
|
+
const out = parseGoalExtractionResult(JSON.stringify({ condition: "The user is satisfied with the result." }));
|
|
118
|
+
expect(out).toBeNull();
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe("parseGoalExtractionResult — security", () => {
|
|
122
|
+
it("rejects conditions starting with '/' to prevent slash command injection", () => {
|
|
123
|
+
expect(parseGoalExtractionResult('{"condition":"/permission grant write"}')).toBeNull();
|
|
124
|
+
});
|
|
125
|
+
it("does not allow a forged '/goal' nested inside another /goal", () => {
|
|
126
|
+
expect(parseGoalExtractionResult('{"condition":"/goal something else"}')).toBeNull();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=goal-extractor.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goal-extractor.test.js","sourceRoot":"","sources":["../../../../../src/connectors/slack/__tests__/goal-extractor.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agents View for Slack — a self-updating Canvas that mirrors OpenRyoko's
|
|
3
|
+
* current sessions, inspired by Claude Code v2.1.139's `claude agents`.
|
|
4
|
+
*
|
|
5
|
+
* Data source: OpenRyoko's own session registry (SQLite via `listSessions()`).
|
|
6
|
+
* NOT `~/.claude/daemon/roster.json` — that file is populated only by
|
|
7
|
+
* `claude --bg` background sessions, and OpenRyoko currently spawns one-shot
|
|
8
|
+
* `claude -p` sessions, so its sessions never appear there.
|
|
9
|
+
*
|
|
10
|
+
* Lifecycle: started by SlackConnector when `agentsCanvas.enabled === true`.
|
|
11
|
+
* Polls the session registry every `pollIntervalMs` (default 30s), renders
|
|
12
|
+
* a Markdown summary, and pushes it into a single Slack Canvas. The canvas
|
|
13
|
+
* is created on first run and reused afterwards; its ID is persisted under
|
|
14
|
+
* `${JINN_HOME}/.agents-canvas-state.json`.
|
|
15
|
+
*/
|
|
16
|
+
import type { App } from "@slack/bolt";
|
|
17
|
+
import type { Session } from "../../shared/types.js";
|
|
18
|
+
export interface AgentsCanvasConfig {
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
/** Slack channel to host the canvas. If unset, a standalone canvas is created. */
|
|
21
|
+
channelId?: string;
|
|
22
|
+
/** Display title. Defaults to "Ryoko Agents View". */
|
|
23
|
+
title?: string;
|
|
24
|
+
/** Polling interval in ms. Default 30s. */
|
|
25
|
+
pollIntervalMs?: number;
|
|
26
|
+
/** Max number of sessions shown per state group. Default 10. */
|
|
27
|
+
maxPerGroup?: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Pure renderer. Exported so it can be unit-tested without spawning a Slack
|
|
31
|
+
* client. `nowMs` is injectable for deterministic snapshots.
|
|
32
|
+
*/
|
|
33
|
+
export declare function renderCanvasMarkdown(sessions: Session[], opts?: {
|
|
34
|
+
title?: string;
|
|
35
|
+
nowMs?: number;
|
|
36
|
+
maxPerGroup?: number;
|
|
37
|
+
}): string;
|
|
38
|
+
/**
|
|
39
|
+
* Manages the lifecycle of the Agents View canvas: periodically polls the
|
|
40
|
+
* session registry, renders Markdown, and pushes it to Slack.
|
|
41
|
+
*/
|
|
42
|
+
export declare class AgentsCanvasUpdater {
|
|
43
|
+
private timer;
|
|
44
|
+
private inflight;
|
|
45
|
+
private stopped;
|
|
46
|
+
private canvasId;
|
|
47
|
+
private lastMarkdown;
|
|
48
|
+
private readonly client;
|
|
49
|
+
private readonly config;
|
|
50
|
+
constructor(app: App, config: AgentsCanvasConfig);
|
|
51
|
+
start(): void;
|
|
52
|
+
stop(): void;
|
|
53
|
+
private tick;
|
|
54
|
+
private publish;
|
|
55
|
+
private createCanvas;
|
|
56
|
+
private fetchExistingChannelCanvasId;
|
|
57
|
+
private editCanvas;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=agents-canvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents-canvas.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/agents-canvas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAKrD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsID;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,OAAO,EAAE,EACnB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GAClE,MAAM,CAoCR;AAwBD;;;GAGG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,YAAY,CAAuB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGrB;gBAEU,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,kBAAkB;IAgBhD,KAAK,IAAI,IAAI;IAiBb,IAAI,IAAI,IAAI;YAQE,IAAI;YAsBJ,OAAO;YAgBP,YAAY;YA8CZ,4BAA4B;YAc5B,UAAU;CAWzB"}
|