openryoko 2026.8.4 → 2026.8.6
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/bin/jimmy.js +63 -0
- package/dist/bin/jimmy.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/gateway/__tests__/notification-wake-delivery.test.d.ts +2 -0
- package/dist/src/gateway/__tests__/notification-wake-delivery.test.d.ts.map +1 -0
- package/dist/src/gateway/__tests__/notification-wake-delivery.test.js +145 -0
- package/dist/src/gateway/__tests__/notification-wake-delivery.test.js.map +1 -0
- package/dist/src/gateway/__tests__/notification-wake-restart.test.d.ts +2 -0
- package/dist/src/gateway/__tests__/notification-wake-restart.test.d.ts.map +1 -0
- package/dist/src/gateway/__tests__/notification-wake-restart.test.js +97 -0
- package/dist/src/gateway/__tests__/notification-wake-restart.test.js.map +1 -0
- package/dist/src/gateway/api.d.ts.map +1 -1
- package/dist/src/gateway/api.js +94 -10
- package/dist/src/gateway/api.js.map +1 -1
- package/dist/src/jobs/__tests__/monitor.test.d.ts +2 -0
- package/dist/src/jobs/__tests__/monitor.test.d.ts.map +1 -0
- package/dist/src/jobs/__tests__/monitor.test.js +142 -0
- package/dist/src/jobs/__tests__/monitor.test.js.map +1 -0
- package/dist/src/jobs/__tests__/notify.test.d.ts +2 -0
- package/dist/src/jobs/__tests__/notify.test.d.ts.map +1 -0
- package/dist/src/jobs/__tests__/notify.test.js +122 -0
- package/dist/src/jobs/__tests__/notify.test.js.map +1 -0
- package/dist/src/jobs/__tests__/state.test.d.ts +2 -0
- package/dist/src/jobs/__tests__/state.test.d.ts.map +1 -0
- package/dist/src/jobs/__tests__/state.test.js +95 -0
- package/dist/src/jobs/__tests__/state.test.js.map +1 -0
- package/dist/src/jobs/monitor.d.ts +7 -0
- package/dist/src/jobs/monitor.d.ts.map +1 -0
- package/dist/src/jobs/monitor.js +153 -0
- package/dist/src/jobs/monitor.js.map +1 -0
- package/dist/src/jobs/notify.d.ts +26 -0
- package/dist/src/jobs/notify.d.ts.map +1 -0
- package/dist/src/jobs/notify.js +132 -0
- package/dist/src/jobs/notify.js.map +1 -0
- package/dist/src/jobs/run.d.ts +21 -0
- package/dist/src/jobs/run.d.ts.map +1 -0
- package/dist/src/jobs/run.js +65 -0
- package/dist/src/jobs/run.js.map +1 -0
- package/dist/src/jobs/state.d.ts +63 -0
- package/dist/src/jobs/state.d.ts.map +1 -0
- package/dist/src/jobs/state.js +126 -0
- package/dist/src/jobs/state.js.map +1 -0
- package/dist/src/sessions/__tests__/context-process-lifetime.test.js +16 -7
- package/dist/src/sessions/__tests__/context-process-lifetime.test.js.map +1 -1
- package/dist/src/sessions/__tests__/detached-jobs-context.test.d.ts +2 -0
- package/dist/src/sessions/__tests__/detached-jobs-context.test.d.ts.map +1 -0
- package/dist/src/sessions/__tests__/detached-jobs-context.test.js +57 -0
- package/dist/src/sessions/__tests__/detached-jobs-context.test.js.map +1 -0
- package/dist/src/sessions/__tests__/manager-orphan-delivery.test.d.ts +2 -0
- package/dist/src/sessions/__tests__/manager-orphan-delivery.test.d.ts.map +1 -0
- package/dist/src/sessions/__tests__/manager-orphan-delivery.test.js +74 -0
- package/dist/src/sessions/__tests__/manager-orphan-delivery.test.js.map +1 -0
- package/dist/src/sessions/__tests__/origin-delivery.test.d.ts +2 -0
- package/dist/src/sessions/__tests__/origin-delivery.test.d.ts.map +1 -0
- package/dist/src/sessions/__tests__/origin-delivery.test.js +116 -0
- package/dist/src/sessions/__tests__/origin-delivery.test.js.map +1 -0
- package/dist/src/sessions/context.d.ts +7 -0
- package/dist/src/sessions/context.d.ts.map +1 -1
- package/dist/src/sessions/context.js +61 -11
- 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 +4 -16
- package/dist/src/sessions/manager.js.map +1 -1
- package/dist/src/sessions/origin-delivery.d.ts +36 -0
- package/dist/src/sessions/origin-delivery.d.ts.map +1 -0
- package/dist/src/sessions/origin-delivery.js +77 -0
- package/dist/src/sessions/origin-delivery.js.map +1 -0
- package/dist/src/sessions/registry.d.ts +7 -0
- package/dist/src/sessions/registry.d.ts.map +1 -1
- package/dist/src/sessions/registry.js +14 -0
- package/dist/src/sessions/registry.js.map +1 -1
- package/dist/src/shared/paths.d.ts +1 -0
- package/dist/src/shared/paths.d.ts.map +1 -1
- package/dist/src/shared/paths.js +1 -0
- package/dist/src/shared/paths.js.map +1 -1
- package/package.json +1 -1
package/dist/bin/jimmy.js
CHANGED
|
@@ -159,6 +159,69 @@ program
|
|
|
159
159
|
await runConfigInteractive(value);
|
|
160
160
|
});
|
|
161
161
|
}
|
|
162
|
+
// 切り離しジョブ(ryoko job run / list — 完了時にセッションを自動起床)
|
|
163
|
+
{
|
|
164
|
+
const jobCmd = program
|
|
165
|
+
.command("job")
|
|
166
|
+
.description("切り離しバックグラウンドジョブ(完了時に元セッションを自動起床)");
|
|
167
|
+
jobCmd
|
|
168
|
+
.command("run")
|
|
169
|
+
.description("コマンドを切り離して実行し、終了時(成功/失敗とも)にセッションへ通知する")
|
|
170
|
+
.requiredOption("--name <name>", "ジョブ名(通知・ログの識別子)")
|
|
171
|
+
.requiredOption("--session <id>", "終了時に起こすセッションID")
|
|
172
|
+
.option("--timeout <sec>", "この秒数を超えたらジョブをkillして失敗通知する")
|
|
173
|
+
.option("--log <path>", "ログファイルのパス(省略時は ~/.ryoko/jobs/logs/<id>.log)")
|
|
174
|
+
.option("--gateway <url>", "gateway URL(loopbackのみ許可。省略時はconfigのポート)")
|
|
175
|
+
.argument("<command...>", "実行するシェルコマンド。クォートした1つの文字列で渡すこと(例: -- 'cd /x && make')")
|
|
176
|
+
.action(async (commandParts, opts) => {
|
|
177
|
+
let timeoutSec;
|
|
178
|
+
if (opts.timeout !== undefined) {
|
|
179
|
+
timeoutSec = Number(opts.timeout);
|
|
180
|
+
if (!Number.isFinite(timeoutSec) || timeoutSec <= 0) {
|
|
181
|
+
console.error(`--timeout must be a positive number of seconds (got "${opts.timeout}")`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const { launchDetachedJob } = await import("../src/jobs/run.js");
|
|
186
|
+
const state = launchDetachedJob({
|
|
187
|
+
name: opts.name,
|
|
188
|
+
sessionId: opts.session,
|
|
189
|
+
command: commandParts.join(" "),
|
|
190
|
+
gatewayUrl: opts.gateway,
|
|
191
|
+
logFile: opts.log,
|
|
192
|
+
timeoutSec,
|
|
193
|
+
});
|
|
194
|
+
console.log(`Detached job started: ${state.id}`);
|
|
195
|
+
console.log(` log: ${state.logFile}`);
|
|
196
|
+
console.log(` monitor: pid ${state.monitorPid}`);
|
|
197
|
+
console.log(` wakes: session ${state.sessionId} on exit (success or failure)`);
|
|
198
|
+
console.log(`This turn can end now — the job survives it and will wake the session when done.`);
|
|
199
|
+
});
|
|
200
|
+
jobCmd
|
|
201
|
+
.command("list")
|
|
202
|
+
.description("ジョブの状態を一覧表示する")
|
|
203
|
+
.action(async () => {
|
|
204
|
+
const { listJobStates, isPidAlive } = await import("../src/jobs/state.js");
|
|
205
|
+
const states = listJobStates();
|
|
206
|
+
if (states.length === 0) {
|
|
207
|
+
console.log("No jobs.");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
for (const s of states) {
|
|
211
|
+
const orphaned = s.status === "running" && !isPidAlive(s.monitorPid) ? " (ORPHANED — monitor dead)" : "";
|
|
212
|
+
const exit = s.exitCode !== undefined && s.exitCode !== null ? ` exit=${s.exitCode}` : s.timedOut ? " timed-out" : "";
|
|
213
|
+
console.log(`${s.id} [${s.status}${orphaned}]${exit} session=${s.sessionId} log=${s.logFile}`);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
jobCmd
|
|
217
|
+
.command("_monitor <id>", { hidden: true })
|
|
218
|
+
.description("内部用: 切り離しモニタープロセスの本体")
|
|
219
|
+
.option("--jobs-dir <dir>")
|
|
220
|
+
.action(async (id, opts) => {
|
|
221
|
+
const { runJobMonitor } = await import("../src/jobs/monitor.js");
|
|
222
|
+
await runJobMonitor(id, { jobsDir: opts.jobsDir });
|
|
223
|
+
});
|
|
224
|
+
}
|
|
162
225
|
program
|
|
163
226
|
.command("chrome-allow")
|
|
164
227
|
.description("Claude Chrome拡張で全サイトを事前承認する")
|
package/dist/bin/jimmy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jimmy.js","sourceRoot":"","sources":["../../bin/jimmy.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"jimmy.js","sourceRoot":"","sources":["../../bin/jimmy.ts"],"names":[],"mappings":"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"}
|
package/dist/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-wake-delivery.test.d.ts","sourceRoot":"","sources":["../../../../src/gateway/__tests__/notification-wake-delivery.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, vi } from "vitest";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import http from "node:http";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
// Regression pin for issue #38 follow-up: a session woken by a notification
|
|
7
|
+
// (detached job finished) runs on the gateway's connector-less web path. Its
|
|
8
|
+
// final answer MUST come back to the original Slack channel/thread stored in
|
|
9
|
+
// reply_context — the 2026-08-04 accident left an externally-shared customer
|
|
10
|
+
// thread waiting on "生成中です" forever.
|
|
11
|
+
//
|
|
12
|
+
// RYOKO_HOME must point at a temp dir BEFORE any module import pulls in
|
|
13
|
+
// shared/paths.js, so every module here is imported dynamically.
|
|
14
|
+
process.env.RYOKO_HOME = fs.mkdtempSync(path.join(os.tmpdir(), "wake-test-home-"));
|
|
15
|
+
const STUB_CONFIG = {
|
|
16
|
+
jinn: { version: "0.0.0" },
|
|
17
|
+
gateway: { port: 0, host: "127.0.0.1" },
|
|
18
|
+
engines: {
|
|
19
|
+
default: "claude",
|
|
20
|
+
claude: { bin: "claude", model: "" },
|
|
21
|
+
codex: { bin: "codex", model: "" },
|
|
22
|
+
},
|
|
23
|
+
connectors: {},
|
|
24
|
+
sessions: {},
|
|
25
|
+
logging: { level: "error", stdout: false, file: "" },
|
|
26
|
+
};
|
|
27
|
+
describe("notification wake-up delivers the reply to the origin connector", () => {
|
|
28
|
+
let server;
|
|
29
|
+
let baseUrl;
|
|
30
|
+
let replyMessage;
|
|
31
|
+
let sessionId;
|
|
32
|
+
beforeAll(async () => {
|
|
33
|
+
const { handleApiRequest } = await import("../api.js");
|
|
34
|
+
const { createSession } = await import("../../sessions/registry.js");
|
|
35
|
+
const { SessionManager } = await import("../../sessions/manager.js");
|
|
36
|
+
const engineRun = vi.fn(async () => ({
|
|
37
|
+
result: "資料が完成しました。アップロード済みです。",
|
|
38
|
+
sessionId: "engine-sess-1",
|
|
39
|
+
numTurns: 1,
|
|
40
|
+
}));
|
|
41
|
+
const mockEngine = { run: engineRun };
|
|
42
|
+
const engines = new Map([["claude", mockEngine]]);
|
|
43
|
+
const sessionManager = new SessionManager(STUB_CONFIG, engines, ["slack"]);
|
|
44
|
+
replyMessage = vi.fn(async () => "posted-ts");
|
|
45
|
+
const fakeSlack = {
|
|
46
|
+
name: "slack",
|
|
47
|
+
replyMessage,
|
|
48
|
+
addReaction: vi.fn(async () => { }),
|
|
49
|
+
getCapabilities: () => ({ threading: true, messageEdits: true, reactions: true, attachments: true }),
|
|
50
|
+
reconstructTarget: (rc) => ({
|
|
51
|
+
channel: typeof rc.channel === "string" ? rc.channel : "",
|
|
52
|
+
thread: typeof rc.thread === "string" ? rc.thread : undefined,
|
|
53
|
+
messageTs: typeof rc.messageTs === "string" ? rc.messageTs : undefined,
|
|
54
|
+
}),
|
|
55
|
+
};
|
|
56
|
+
const session = createSession({
|
|
57
|
+
engine: "claude",
|
|
58
|
+
source: "slack",
|
|
59
|
+
sourceRef: "C_EXTERNAL",
|
|
60
|
+
connector: "slack",
|
|
61
|
+
sessionKey: "slack:C_EXTERNAL:100.200",
|
|
62
|
+
replyContext: { channel: "C_EXTERNAL", thread: "100.200", messageTs: "100.300" },
|
|
63
|
+
transportMeta: { channelExternal: true },
|
|
64
|
+
prompt: "資料を作って",
|
|
65
|
+
});
|
|
66
|
+
sessionId = session.id;
|
|
67
|
+
const context = {
|
|
68
|
+
config: STUB_CONFIG,
|
|
69
|
+
getConfig: () => STUB_CONFIG,
|
|
70
|
+
sessionManager,
|
|
71
|
+
startTime: 0,
|
|
72
|
+
emit: () => { },
|
|
73
|
+
connectors: new Map([["slack", fakeSlack]]),
|
|
74
|
+
};
|
|
75
|
+
server = http.createServer((req, res) => {
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
|
+
handleApiRequest(req, res, context);
|
|
78
|
+
});
|
|
79
|
+
await new Promise((r) => server.listen(0, "127.0.0.1", r));
|
|
80
|
+
const addr = server.address();
|
|
81
|
+
baseUrl = typeof addr === "object" && addr ? `http://127.0.0.1:${addr.port}` : "";
|
|
82
|
+
});
|
|
83
|
+
afterAll(async () => {
|
|
84
|
+
await new Promise((resolve, reject) => server.close((e) => (e ? reject(e) : resolve())));
|
|
85
|
+
fs.rmSync(process.env.RYOKO_HOME, { recursive: true, force: true });
|
|
86
|
+
});
|
|
87
|
+
it("wakes the session and replies into the original channel/thread", async () => {
|
|
88
|
+
const res = await fetch(`${baseUrl}/api/sessions/${sessionId}/message`, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: { "content-type": "application/json" },
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
message: '✅ Detached job "pdf-build" completed successfully (exit 0).',
|
|
93
|
+
role: "notification",
|
|
94
|
+
}),
|
|
95
|
+
});
|
|
96
|
+
expect(res.status).toBe(200);
|
|
97
|
+
// The engine run + delivery happen asynchronously after the 200.
|
|
98
|
+
await vi.waitFor(() => {
|
|
99
|
+
expect(replyMessage).toHaveBeenCalledTimes(1);
|
|
100
|
+
}, { timeout: 10_000 });
|
|
101
|
+
const [target, text] = replyMessage.mock.calls[0];
|
|
102
|
+
expect(target).toMatchObject({ channel: "C_EXTERNAL", thread: "100.200" });
|
|
103
|
+
expect(text).toBe("資料が完成しました。アップロード済みです。");
|
|
104
|
+
});
|
|
105
|
+
it("a duplicate dedupeKey does not enqueue a second turn (monitor retry after lost response)", async () => {
|
|
106
|
+
replyMessage.mockClear();
|
|
107
|
+
const body = JSON.stringify({
|
|
108
|
+
message: '✅ Detached job "dedupe-job" completed successfully (exit 0).',
|
|
109
|
+
role: "notification",
|
|
110
|
+
dedupeKey: "job:dedupe-job-1",
|
|
111
|
+
});
|
|
112
|
+
const first = await fetch(`${baseUrl}/api/sessions/${sessionId}/message`, {
|
|
113
|
+
method: "POST",
|
|
114
|
+
headers: { "content-type": "application/json" },
|
|
115
|
+
body,
|
|
116
|
+
});
|
|
117
|
+
expect(first.status).toBe(200);
|
|
118
|
+
expect((await first.json()).status).toBe("queued");
|
|
119
|
+
const second = await fetch(`${baseUrl}/api/sessions/${sessionId}/message`, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: { "content-type": "application/json" },
|
|
122
|
+
body,
|
|
123
|
+
});
|
|
124
|
+
expect(second.status).toBe(200);
|
|
125
|
+
expect((await second.json()).status).toBe("duplicate");
|
|
126
|
+
await vi.waitFor(() => {
|
|
127
|
+
expect(replyMessage).toHaveBeenCalledTimes(1); // one turn, one delivery
|
|
128
|
+
}, { timeout: 10_000 });
|
|
129
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
130
|
+
expect(replyMessage).toHaveBeenCalledTimes(1);
|
|
131
|
+
});
|
|
132
|
+
it("a plain user message on the web path does NOT auto-post to the connector", async () => {
|
|
133
|
+
replyMessage.mockClear();
|
|
134
|
+
const res = await fetch(`${baseUrl}/api/sessions/${sessionId}/message`, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: { "content-type": "application/json" },
|
|
137
|
+
body: JSON.stringify({ message: "operator typing in web UI", role: "user" }),
|
|
138
|
+
});
|
|
139
|
+
expect(res.status).toBe(200);
|
|
140
|
+
// Give the async run time to complete, then confirm no connector post.
|
|
141
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
142
|
+
expect(replyMessage).not.toHaveBeenCalled();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
//# sourceMappingURL=notification-wake-delivery.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-wake-delivery.test.js","sourceRoot":"","sources":["../../../../src/gateway/__tests__/notification-wake-delivery.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-wake-restart.test.d.ts","sourceRoot":"","sources":["../../../../src/gateway/__tests__/notification-wake-restart.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll, vi } from "vitest";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
// Gateway-restart recovery for notification wake-ups (issue #38 follow-up):
|
|
6
|
+
// the job monitor got its 200 (message persisted + queue item enqueued) and
|
|
7
|
+
// will never re-send. If the gateway dies before running the turn, the resume
|
|
8
|
+
// pass must replay the queue item FOR CONNECTOR-ORIGIN SESSIONS TOO — and
|
|
9
|
+
// with origin-connector delivery, or the customer thread stays silent.
|
|
10
|
+
process.env.RYOKO_HOME = fs.mkdtempSync(path.join(os.tmpdir(), "wake-restart-home-"));
|
|
11
|
+
const STUB_CONFIG = {
|
|
12
|
+
jinn: { version: "0.0.0" },
|
|
13
|
+
gateway: { port: 0, host: "127.0.0.1" },
|
|
14
|
+
engines: {
|
|
15
|
+
default: "claude",
|
|
16
|
+
claude: { bin: "claude", model: "" },
|
|
17
|
+
codex: { bin: "codex", model: "" },
|
|
18
|
+
},
|
|
19
|
+
connectors: {},
|
|
20
|
+
sessions: {},
|
|
21
|
+
logging: { level: "error", stdout: false, file: "" },
|
|
22
|
+
};
|
|
23
|
+
describe("resumePendingWebQueueItems — connector-origin notification wake-ups", () => {
|
|
24
|
+
let replyMessage;
|
|
25
|
+
let engineRun;
|
|
26
|
+
beforeAll(async () => {
|
|
27
|
+
const { resumePendingWebQueueItems } = await import("../api.js");
|
|
28
|
+
const { createSession, insertMessage, enqueueQueueItem } = await import("../../sessions/registry.js");
|
|
29
|
+
const { SessionManager } = await import("../../sessions/manager.js");
|
|
30
|
+
engineRun = vi.fn(async () => ({ result: "遅れましたが完成しました", sessionId: "es-1", numTurns: 1 }));
|
|
31
|
+
const engines = new Map([["claude", { run: engineRun }]]);
|
|
32
|
+
const sessionManager = new SessionManager(STUB_CONFIG, engines, ["slack"]);
|
|
33
|
+
replyMessage = vi.fn(async () => "ts");
|
|
34
|
+
const fakeSlack = {
|
|
35
|
+
name: "slack",
|
|
36
|
+
replyMessage,
|
|
37
|
+
addReaction: vi.fn(async () => { }),
|
|
38
|
+
getCapabilities: () => ({ threading: true, messageEdits: true, reactions: true, attachments: true }),
|
|
39
|
+
reconstructTarget: (rc) => ({
|
|
40
|
+
channel: typeof rc.channel === "string" ? rc.channel : "",
|
|
41
|
+
thread: typeof rc.thread === "string" ? rc.thread : undefined,
|
|
42
|
+
}),
|
|
43
|
+
};
|
|
44
|
+
// Simulate the pre-restart state the monitor left behind: notification
|
|
45
|
+
// message persisted + queue item pending, but no turn ever ran.
|
|
46
|
+
const wakeText = '✅ Detached job "pdf" completed successfully (exit 0).';
|
|
47
|
+
const slackSession = createSession({
|
|
48
|
+
engine: "claude",
|
|
49
|
+
source: "slack",
|
|
50
|
+
sourceRef: "C_CUST",
|
|
51
|
+
connector: "slack",
|
|
52
|
+
sessionKey: "slack:C_CUST:1.2",
|
|
53
|
+
replyContext: { channel: "C_CUST", thread: "1.2" },
|
|
54
|
+
transportMeta: { channelExternal: true },
|
|
55
|
+
prompt: "資料を作って",
|
|
56
|
+
});
|
|
57
|
+
insertMessage(slackSession.id, "notification", wakeText);
|
|
58
|
+
enqueueQueueItem(slackSession.id, slackSession.sessionKey, wakeText);
|
|
59
|
+
// Control: a connector session with a pending USER prompt must stay
|
|
60
|
+
// untouched (pre-existing behavior — the connector route owns it).
|
|
61
|
+
const userSession = createSession({
|
|
62
|
+
engine: "claude",
|
|
63
|
+
source: "slack",
|
|
64
|
+
sourceRef: "C_OTHER",
|
|
65
|
+
connector: "slack",
|
|
66
|
+
sessionKey: "slack:C_OTHER:9.9",
|
|
67
|
+
replyContext: { channel: "C_OTHER", thread: "9.9" },
|
|
68
|
+
prompt: "こんにちは",
|
|
69
|
+
});
|
|
70
|
+
insertMessage(userSession.id, "user", "続きをやって");
|
|
71
|
+
enqueueQueueItem(userSession.id, userSession.sessionKey, "続きをやって");
|
|
72
|
+
const context = {
|
|
73
|
+
config: STUB_CONFIG,
|
|
74
|
+
getConfig: () => STUB_CONFIG,
|
|
75
|
+
sessionManager,
|
|
76
|
+
startTime: 0,
|
|
77
|
+
emit: () => { },
|
|
78
|
+
connectors: new Map([["slack", fakeSlack]]),
|
|
79
|
+
};
|
|
80
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
81
|
+
resumePendingWebQueueItems(context);
|
|
82
|
+
});
|
|
83
|
+
afterAll(() => {
|
|
84
|
+
fs.rmSync(process.env.RYOKO_HOME, { recursive: true, force: true });
|
|
85
|
+
});
|
|
86
|
+
it("replays the notification wake-up and delivers to the original thread", async () => {
|
|
87
|
+
await vi.waitFor(() => {
|
|
88
|
+
expect(replyMessage).toHaveBeenCalledTimes(1);
|
|
89
|
+
}, { timeout: 10_000 });
|
|
90
|
+
const [target, text] = replyMessage.mock.calls[0];
|
|
91
|
+
expect(target).toMatchObject({ channel: "C_CUST", thread: "1.2" });
|
|
92
|
+
expect(text).toBe("遅れましたが完成しました");
|
|
93
|
+
// Only the notification item ran — the user-prompt item was left alone.
|
|
94
|
+
expect(engineRun).toHaveBeenCalledTimes(1);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
//# sourceMappingURL=notification-wake-restart.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-wake-restart.test.js","sourceRoot":"","sources":["../../../../src/gateway/__tests__/notification-wake-restart.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACvE,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,4EAA4E;AAC5E,4EAA4E;AAC5E,8EAA8E;AAC9E,0EAA0E;AAC1E,uEAAuE;AACvE,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC;AAEtF,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE;IAC1B,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE;IACvC,OAAO,EAAE;QACP,OAAO,EAAE,QAAQ;QACjB,MAAM,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;QACpC,KAAK,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE;KACnC;IACD,UAAU,EAAE,EAAE;IACd,QAAQ,EAAE,EAAE;IACZ,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE;CAC5B,CAAC;AAE3B,QAAQ,CAAC,qEAAqE,EAAE,GAAG,EAAE;IACnF,IAAI,YAAsC,CAAC;IAC3C,IAAI,SAAmC,CAAC;IAExC,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,EAAE,0BAA0B,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QACjE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,4BAA4B,CAAC,CAAC;QACtG,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;QAErE,SAAS,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5F,MAAM,OAAO,GAAG,IAAI,GAAG,CAAiB,CAAC,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,SAAS,EAAuB,CAAC,CAAC,CAAC,CAAC;QAC/F,MAAM,cAAc,GAAG,IAAI,cAAc,CAAC,WAAW,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAE3E,YAAY,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,SAAS,GAAG;YAChB,IAAI,EAAE,OAAO;YACb,YAAY;YACZ,WAAW,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC;YAClC,eAAe,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;YACpG,iBAAiB,EAAE,CAAC,EAA2B,EAAE,EAAE,CAAC,CAAC;gBACnD,OAAO,EAAE,OAAO,EAAE,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;gBACzD,MAAM,EAAE,OAAO,EAAE,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;aAC9D,CAAC;SACqB,CAAC;QAE1B,uEAAuE;QACvE,gEAAgE;QAChE,MAAM,QAAQ,GAAG,uDAAuD,CAAC;QACzE,MAAM,YAAY,GAAG,aAAa,CAAC;YACjC,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,OAAO;YACf,SAAS,EAAE,QAAQ;YACnB,SAAS,EAAE,OAAO;YAClB,UAAU,EAAE,kBAAkB;YAC9B,YAAY,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE;YAClD,aAAa,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE;YACxC,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAC;QACH,aAAa,CAAC,YAAY,CAAC,EAAE,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;QACzD,gBAAgB,CAAC,YAAY,CAAC,EAAE,EAAE,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAErE,oEAAoE;QACpE,mEAAmE;QACnE,MAAM,WAAW,GAAG,aAAa,CAAC;YAChC,MAAM,EAAE,QAAQ;YAChB,MAAM,EAAE,OAAO;YACf,SAAS,EAAE,SAAS;YACpB,SAAS,EAAE,OAAO;YAClB,UAAU,EAAE,mBAAmB;YAC/B,YAAY,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE;YACnD,MAAM,EAAE,OAAO;SAChB,CAAC,CAAC;QACH,aAAa,CAAC,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChD,gBAAgB,CAAC,WAAW,CAAC,EAAE,EAAE,WAAW,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAEnE,MAAM,OAAO,GAAG;YACd,MAAM,EAAE,WAAW;YACnB,SAAS,EAAE,GAAG,EAAE,CAAC,WAAW;YAC5B,cAAc;YACd,SAAS,EAAE,CAAC;YACZ,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;YACd,UAAU,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC;SAC5C,CAAC;QACF,8DAA8D;QAC9D,0BAA0B,CAAC,OAAc,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,GAAG,EAAE;QACZ,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,UAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YACpB,MAAM,CAAC,YAAY,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAChD,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAExB,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAiD,CAAC;QAClG,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;QACnE,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAClC,wEAAwE;QACxE,MAAM,CAAC,SAAS,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/gateway/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,IAAI,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhF,OAAO,KAAK,EAAoC,UAAU,EAAmB,MAAM,oBAAoB,CAAC;AAExG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/gateway/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,IAAI,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhF,OAAO,KAAK,EAAoC,UAAU,EAAmB,MAAM,oBAAoB,CAAC;AAExG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAuD7D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,UAAU,CAAC;IAC5B,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,oBAAoB,EAAE,SAAS,CAAC,CAAC;IAChE,wBAAwB,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACrG;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAChG;;;;;;OAMG;IACH,2BAA2B,CAAC,EAAE,MAAM,IAAI,CAAC;IACzC;;;;OAIG;IACH,gCAAgC,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9C;;;;;OAKG;IACH,YAAY,CAAC,EAAE,OAAO,oBAAoB,EAAE,YAAY,CAAC;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CA4CpE;AAoMD,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA+BnH;AA4CD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,WAAW,EAChB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAoqDf"}
|
package/dist/src/gateway/api.js
CHANGED
|
@@ -5,8 +5,9 @@ import path from "node:path";
|
|
|
5
5
|
import yaml from "js-yaml";
|
|
6
6
|
import { isInterruptibleEngine } from "../shared/types.js";
|
|
7
7
|
import { buildContext } from "../sessions/context.js";
|
|
8
|
-
import { initDb, listSessions, getSession, createSession, updateSession, deleteSession, deleteSessions, duplicateSession, insertMessage, getMessages, enqueueQueueItem, cancelQueueItem, getQueueItems, cancelAllPendingQueueItems, listAllPendingQueueItems, getFile, } from "../sessions/registry.js";
|
|
8
|
+
import { initDb, listSessions, getSession, createSession, updateSession, deleteSession, deleteSessions, duplicateSession, insertMessage, getMessages, enqueueQueueItem, insertNotificationWithQueueItem, cancelQueueItem, getQueueItems, cancelAllPendingQueueItems, listAllPendingQueueItems, getFile, } from "../sessions/registry.js";
|
|
9
9
|
import { forkEngineSession } from "../sessions/fork.js";
|
|
10
|
+
import { deliverToOriginConnector, isUndeliveredToOrigin, recordFailedOriginDelivery } from "../sessions/origin-delivery.js";
|
|
10
11
|
import { CONFIG_PATH, CRON_RUNS, ORG_DIR, SKILLS_DIR, LOGS_DIR, TMP_DIR, FILES_DIR, } from "../shared/paths.js";
|
|
11
12
|
import { logger } from "../shared/logger.js";
|
|
12
13
|
import { getSttStatus, downloadModel, transcribe as sttTranscribe, resolveLanguages, WHISPER_LANGUAGES } from "../stt/stt.js";
|
|
@@ -37,8 +38,20 @@ export function resumePendingWebQueueItems(context) {
|
|
|
37
38
|
cancelQueueItem(item.id);
|
|
38
39
|
continue;
|
|
39
40
|
}
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
// Connector-origin sessions normally get their runs from the connector
|
|
42
|
+
// route, EXCEPT notification wake-ups (detached jobs, child callbacks):
|
|
43
|
+
// the job monitor already got its 200 and will never re-send, so a
|
|
44
|
+
// restart here would strand the wake-up forever. Detect those by the
|
|
45
|
+
// persisted notification message matching the queued prompt and resume
|
|
46
|
+
// them with origin-connector delivery.
|
|
47
|
+
let deliverToConnector = false;
|
|
48
|
+
if (session.source !== "web") {
|
|
49
|
+
const messages = getMessages(session.id);
|
|
50
|
+
const match = [...messages].reverse().find((m) => m.content === item.prompt);
|
|
51
|
+
if (match?.role !== "notification")
|
|
52
|
+
continue;
|
|
53
|
+
deliverToConnector = true;
|
|
54
|
+
}
|
|
42
55
|
session = maybeRevertEngineOverride(session);
|
|
43
56
|
const config = context.getConfig();
|
|
44
57
|
const engine = context.sessionManager.getEngine(session.engine);
|
|
@@ -49,7 +62,7 @@ export function resumePendingWebQueueItems(context) {
|
|
|
49
62
|
}
|
|
50
63
|
// Ensure the session is in a runnable state
|
|
51
64
|
updateSession(session.id, { status: "running", lastActivity: new Date().toISOString(), lastError: null });
|
|
52
|
-
dispatchWebSessionRun(session, item.prompt, engine, config, context, { queueItemId: item.id });
|
|
65
|
+
dispatchWebSessionRun(session, item.prompt, engine, config, context, { queueItemId: item.id, deliverToConnector });
|
|
53
66
|
resumed++;
|
|
54
67
|
}
|
|
55
68
|
if (resumed > 0) {
|
|
@@ -96,11 +109,24 @@ function maybeRevertEngineOverride(session) {
|
|
|
96
109
|
lastError: null,
|
|
97
110
|
}) ?? session;
|
|
98
111
|
}
|
|
112
|
+
// In-memory idempotency keys for notification wake-ups. The persisted-message
|
|
113
|
+
// comparison in the /message handler covers gateway restarts; this map covers
|
|
114
|
+
// the common case cheaply and caps unbounded growth by TTL pruning on write.
|
|
115
|
+
const seenDedupeKeys = new Map();
|
|
116
|
+
const DEDUPE_TTL_MS = 60 * 60 * 1000;
|
|
117
|
+
function rememberDedupeKey(key) {
|
|
118
|
+
const now = Date.now();
|
|
119
|
+
for (const [k, t] of seenDedupeKeys) {
|
|
120
|
+
if (now - t > DEDUPE_TTL_MS)
|
|
121
|
+
seenDedupeKeys.delete(k);
|
|
122
|
+
}
|
|
123
|
+
seenDedupeKeys.set(key, now);
|
|
124
|
+
}
|
|
99
125
|
function dispatchWebSessionRun(session, prompt, engine, config, context, opts) {
|
|
100
126
|
const run = async () => {
|
|
101
127
|
await context.sessionManager.getQueue().enqueue(session.sessionKey || session.sourceRef, async () => {
|
|
102
128
|
context.emit("session:started", { sessionId: session.id });
|
|
103
|
-
await runWebSession(session, prompt, engine, config, context, opts?.attachments);
|
|
129
|
+
await runWebSession(session, prompt, engine, config, context, opts?.attachments, opts?.deliverToConnector);
|
|
104
130
|
}, opts?.queueItemId);
|
|
105
131
|
};
|
|
106
132
|
const launch = () => {
|
|
@@ -740,12 +766,39 @@ export async function handleApiRequest(req, res, context) {
|
|
|
740
766
|
// Allow internal callers (e.g. child session callbacks) to specify a non-user role
|
|
741
767
|
const messageRole = body.role === "notification" ? "notification" : "user";
|
|
742
768
|
const isNotification = messageRole === "notification";
|
|
769
|
+
// Notification wake-ups are a loopback-only mechanism (job monitors,
|
|
770
|
+
// child-session callbacks). A remote caller must not be able to wake an
|
|
771
|
+
// arbitrary session — the woken turn can post into its origin Slack
|
|
772
|
+
// conversation, so this would be a remote-to-customer-channel bridge.
|
|
773
|
+
if (isNotification && (!req.socket.remoteAddress || !HOOK_LOOPBACK.has(req.socket.remoteAddress))) {
|
|
774
|
+
return json(res, { error: "notification role is loopback-only" }, 403);
|
|
775
|
+
}
|
|
776
|
+
// Idempotency: a job monitor retries its wake-up when a response is
|
|
777
|
+
// lost after the gateway already accepted it. Same dedupeKey (+ an
|
|
778
|
+
// identical persisted notification, which survives restarts) → don't
|
|
779
|
+
// enqueue a second engine turn. The key is only REMEMBERED after the
|
|
780
|
+
// message + queue item are durably persisted below, so a failure
|
|
781
|
+
// before that point never turns the retry into a false duplicate.
|
|
782
|
+
const dedupeKey = typeof body.dedupeKey === "string" && body.dedupeKey.trim() ? body.dedupeKey.trim() : null;
|
|
783
|
+
const dedupedNotification = isNotification && dedupeKey !== null;
|
|
784
|
+
if (dedupedNotification) {
|
|
785
|
+
const duplicate = seenDedupeKeys.has(dedupeKey)
|
|
786
|
+
|| getMessages(session.id).some((m) => m.role === "notification" && m.content === prompt);
|
|
787
|
+
if (duplicate) {
|
|
788
|
+
return json(res, { status: "duplicate", sessionId: session.id });
|
|
789
|
+
}
|
|
790
|
+
}
|
|
743
791
|
const config = context.getConfig();
|
|
744
792
|
const engine = context.sessionManager.getEngine(session.engine);
|
|
745
793
|
if (!engine)
|
|
746
794
|
return serverError(res, `Engine "${session.engine}" not available`);
|
|
747
|
-
// Persist the message immediately
|
|
748
|
-
|
|
795
|
+
// Persist the message immediately. Deduped notifications defer this to
|
|
796
|
+
// the atomic message+queue-item write below — the duplicate check above
|
|
797
|
+
// treats "message exists" as "turn queued", so the two writes must
|
|
798
|
+
// never be separable by a crash.
|
|
799
|
+
if (!dedupedNotification) {
|
|
800
|
+
insertMessage(session.id, messageRole, prompt);
|
|
801
|
+
}
|
|
749
802
|
// Emit notification event for UI display (renders as system banner, not user bubble)
|
|
750
803
|
if (isNotification) {
|
|
751
804
|
context.emit("session:notification", { sessionId: session.id, message: prompt });
|
|
@@ -789,9 +842,20 @@ export async function handleApiRequest(req, res, context) {
|
|
|
789
842
|
context.sessionManager.getQueue().clearCancelled(session.sessionKey || session.sourceRef || session.id);
|
|
790
843
|
const attachmentPaths = resolveAttachmentPaths(body.attachments);
|
|
791
844
|
const sessionKey = session.sessionKey || session.sourceRef || session.id;
|
|
792
|
-
const queueItemId =
|
|
845
|
+
const queueItemId = dedupedNotification
|
|
846
|
+
? insertNotificationWithQueueItem(session.id, sessionKey, prompt)
|
|
847
|
+
: enqueueQueueItem(session.id, sessionKey, prompt);
|
|
848
|
+
if (dedupedNotification)
|
|
849
|
+
rememberDedupeKey(dedupeKey);
|
|
793
850
|
context.emit("queue:updated", { sessionId: session.id, sessionKey });
|
|
794
|
-
dispatchWebSessionRun(session, prompt, engine, config, context, {
|
|
851
|
+
dispatchWebSessionRun(session, prompt, engine, config, context, {
|
|
852
|
+
queueItemId,
|
|
853
|
+
attachments: attachmentPaths.length > 0 ? attachmentPaths : undefined,
|
|
854
|
+
// A notification wake-up (detached job finished, child callback) runs
|
|
855
|
+
// on this connector-less path; deliver the answer back to the origin
|
|
856
|
+
// conversation so e.g. a Slack thread is not left waiting silently.
|
|
857
|
+
deliverToConnector: isNotification,
|
|
858
|
+
});
|
|
795
859
|
return json(res, { status: "queued", sessionId: session.id });
|
|
796
860
|
}
|
|
797
861
|
// GET /api/cron
|
|
@@ -2056,7 +2120,12 @@ function loadTranscriptMessages(engineSessionId) {
|
|
|
2056
2120
|
}
|
|
2057
2121
|
return [];
|
|
2058
2122
|
}
|
|
2059
|
-
async function runWebSession(session, prompt, engine, config, context, attachments
|
|
2123
|
+
async function runWebSession(session, prompt, engine, config, context, attachments,
|
|
2124
|
+
/** Deliver the final answer to the session's origin connector (Slack thread
|
|
2125
|
+
* etc.). Set for notification-triggered wake-ups: this run path has no
|
|
2126
|
+
* connector of its own, so without explicit delivery a woken Slack session
|
|
2127
|
+
* would compute its reply and post it nowhere (issue #38 follow-up). */
|
|
2128
|
+
deliverToConnector) {
|
|
2060
2129
|
const currentSession = getSession(session.id);
|
|
2061
2130
|
if (!currentSession) {
|
|
2062
2131
|
logger.info(`Skipping deleted web session ${session.id} before run start`);
|
|
@@ -2260,6 +2329,11 @@ async function runWebSession(session, prompt, engine, config, context, attachmen
|
|
|
2260
2329
|
});
|
|
2261
2330
|
if (completedFallback) {
|
|
2262
2331
|
notifyParentSession(completedFallback, { result: fallbackResult.result, error: fallbackResult.error ?? null, cost: fallbackResult.cost, durationMs: fallbackResult.durationMs }, { alwaysNotify: employee?.alwaysNotify });
|
|
2332
|
+
if (deliverToConnector && fallbackResult.result && !fallbackResult.error) {
|
|
2333
|
+
const delivery = await deliverToOriginConnector(completedFallback, fallbackResult.result, context.connectors);
|
|
2334
|
+
if (isUndeliveredToOrigin(delivery, completedFallback))
|
|
2335
|
+
recordFailedOriginDelivery(completedFallback, context.emit);
|
|
2336
|
+
}
|
|
2263
2337
|
}
|
|
2264
2338
|
context.emit("session:completed", {
|
|
2265
2339
|
sessionId: currentSession.id,
|
|
@@ -2375,6 +2449,11 @@ async function runWebSession(session, prompt, engine, config, context, attachmen
|
|
|
2375
2449
|
notifyRateLimitResumed(completedAfterRetry);
|
|
2376
2450
|
notifyDiscordChannel(`✅ Claude usage limit cleared. Session ${currentSession.id}${currentSession.employee ? ` (${currentSession.employee})` : ""} resumed.`);
|
|
2377
2451
|
notifyParentSession(completedAfterRetry, { result: retryResult.result, error: retryResult.error ?? null, cost: retryResult.cost, durationMs: retryResult.durationMs }, { alwaysNotify: employee?.alwaysNotify });
|
|
2452
|
+
if (deliverToConnector && retryResult.result && !retryResult.error) {
|
|
2453
|
+
const delivery = await deliverToOriginConnector(completedAfterRetry, retryResult.result, context.connectors);
|
|
2454
|
+
if (isUndeliveredToOrigin(delivery, completedAfterRetry))
|
|
2455
|
+
recordFailedOriginDelivery(completedAfterRetry, context.emit);
|
|
2456
|
+
}
|
|
2378
2457
|
}
|
|
2379
2458
|
context.emit("session:completed", {
|
|
2380
2459
|
sessionId: currentSession.id,
|
|
@@ -2431,6 +2510,11 @@ async function runWebSession(session, prompt, engine, config, context, attachmen
|
|
|
2431
2510
|
}
|
|
2432
2511
|
if (completedSession) {
|
|
2433
2512
|
notifyParentSession(completedSession, { result: result.result, error: result.error ?? null, cost: result.cost, durationMs: result.durationMs }, { alwaysNotify: employee?.alwaysNotify });
|
|
2513
|
+
if (deliverToConnector && result.result && !result.error) {
|
|
2514
|
+
const delivery = await deliverToOriginConnector(completedSession, result.result, context.connectors);
|
|
2515
|
+
if (isUndeliveredToOrigin(delivery, completedSession))
|
|
2516
|
+
recordFailedOriginDelivery(completedSession, context.emit);
|
|
2517
|
+
}
|
|
2434
2518
|
}
|
|
2435
2519
|
context.emit("session:completed", {
|
|
2436
2520
|
sessionId: currentSession.id,
|