talon-agent 4.3.0 → 4.3.2
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/package.json +1 -1
- package/src/app.ts +1 -1
- package/src/bootstrap.ts +5 -2
- package/src/cli/skill.ts +2 -2
- package/src/core/background/cron-spec.ts +1 -1
- package/src/core/background/cron.ts +3 -5
- package/src/core/background/heartbeat/agent.ts +1 -1
- package/src/core/background/triggers/command.ts +1 -1
- package/src/core/background/triggers/exit.ts +1 -1
- package/src/core/background/triggers/output.ts +1 -1
- package/src/core/background/triggers/resume.ts +1 -1
- package/src/core/background/triggers/spawn.ts +1 -1
- package/src/core/engine/gateway-actions/cron.ts +18 -18
- package/src/core/engine/gateway-actions/goals.ts +10 -10
- package/src/core/engine/gateway-actions/history.ts +10 -17
- package/src/core/engine/gateway-actions/index.ts +9 -2
- package/src/core/engine/gateway-actions/models.ts +6 -6
- package/src/core/engine/gateway-actions/plugins.ts +2 -8
- package/src/core/engine/gateway-actions/scripts.ts +1 -1
- package/src/core/engine/gateway-actions/skills.ts +1 -1
- package/src/core/engine/gateway-actions/triggers.ts +18 -27
- package/src/core/engine/gateway-actions/types.ts +11 -1
- package/src/core/engine/gateway-actions/{shared.ts → validation.ts} +5 -6
- package/src/core/engine/gateway.ts +12 -2
- package/src/core/prompt/assemble.ts +2 -2
- package/src/core/scripts/runner.ts +1 -1
- package/src/core/soul/forgetting.ts +2 -2
- package/src/core/soul/salience.ts +8 -4
- package/src/core/weaver/loom.ts +11 -0
- package/src/frontend/discord/actions/{shared.ts → buttons.ts} +3 -56
- package/src/frontend/discord/actions/channels.ts +34 -0
- package/src/frontend/discord/actions/chat-info.ts +1 -1
- package/src/frontend/discord/actions/index.ts +1 -1
- package/src/frontend/discord/actions/media.ts +1 -1
- package/src/frontend/discord/actions/messaging.ts +4 -2
- package/src/frontend/discord/actions/try-action.ts +24 -0
- package/src/frontend/discord/admin.ts +2 -2
- package/src/frontend/discord/callbacks/components/settings.ts +1 -1
- package/src/frontend/discord/callbacks/modals.ts +1 -1
- package/src/frontend/discord/commands/admin.ts +2 -2
- package/src/frontend/discord/commands/info.ts +2 -2
- package/src/frontend/discord/commands/{shared.ts → interaction.ts} +2 -1
- package/src/frontend/discord/commands/router.ts +1 -1
- package/src/frontend/discord/commands/session.ts +2 -2
- package/src/frontend/discord/commands/settings.ts +2 -2
- package/src/frontend/discord/{helpers.ts → render.ts} +2 -1
- package/src/frontend/native/extensions.ts +1 -1
- package/src/frontend/teams/chat-discovery.ts +69 -0
- package/src/frontend/teams/commands.ts +141 -0
- package/src/frontend/teams/index.ts +24 -350
- package/src/frontend/teams/outbound.ts +44 -0
- package/src/frontend/teams/poll.ts +101 -0
- package/src/frontend/teams/runtime.ts +51 -0
- package/src/frontend/teams/turn.ts +97 -0
- package/src/frontend/telegram/actions/messaging.ts +1 -1
- package/src/frontend/telegram/actions/shared.ts +4 -4
- package/src/frontend/telegram/admin.ts +1 -1
- package/src/frontend/telegram/sticker-library.ts +2 -2
- package/src/frontend/whatsapp/actions/account.ts +3 -6
- package/src/frontend/whatsapp/actions/chat-info.ts +2 -1
- package/src/frontend/whatsapp/actions/coerce.ts +30 -0
- package/src/frontend/whatsapp/actions/history.ts +1 -1
- package/src/frontend/whatsapp/actions/media-upload.ts +82 -0
- package/src/frontend/whatsapp/actions/media.ts +3 -8
- package/src/frontend/whatsapp/actions/messaging.ts +2 -7
- package/src/frontend/whatsapp/actions/moderation.ts +2 -1
- package/src/frontend/whatsapp/actions/{shared.ts → send.ts} +4 -124
- package/src/frontend/whatsapp/actions/try-action.ts +24 -0
- package/src/frontend/whatsapp/commands.ts +1 -1
- package/src/frontend/whatsapp/inbound.ts +1 -1
- package/src/frontend/whatsapp/index.ts +1 -1
- package/src/storage/repositories/cron-repo.ts +1 -1
- package/src/storage/repositories/goals-repo.ts +1 -1
- package/src/storage/repositories/scripts-repo.ts +1 -1
- package/src/storage/repositories/triggers-repo.ts +1 -1
- package/src/storage/sql/schema.sql +1 -1
- package/src/storage/sql/statements.generated.ts +2 -2
- package/src/storage/sql/triggers.sql +1 -1
- package/src/util/chat-id.ts +13 -0
- /package/src/storage/{cron-store.ts → cron.ts} +0 -0
- /package/src/storage/{goal-store.ts → goals.ts} +0 -0
- /package/src/storage/{scheduled-store.ts → scheduled.ts} +0 -0
- /package/src/storage/{script-store.ts → scripts.ts} +0 -0
- /package/src/storage/{skill-store.ts → skills.ts} +0 -0
- /package/src/storage/{sticker-store.ts → stickers.ts} +0 -0
- /package/src/storage/{trigger-store.ts → triggers.ts} +0 -0
package/package.json
CHANGED
package/src/app.ts
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
runStartupCatchup,
|
|
24
24
|
} from "./core/background/cron.js";
|
|
25
25
|
import { shutdownTriggers } from "./core/background/triggers/index.js";
|
|
26
|
-
import { pruneSettledTriggers } from "./storage/
|
|
26
|
+
import { pruneSettledTriggers } from "./storage/triggers.js";
|
|
27
27
|
import { startWatchdog, stopWatchdog } from "./util/watchdog.js";
|
|
28
28
|
import { spawnSuccessor } from "./util/respawn.js";
|
|
29
29
|
import { log, logError, logWarn } from "./util/log.js";
|
package/src/bootstrap.ts
CHANGED
|
@@ -13,8 +13,8 @@ import { loadConfig, rebuildSystemPrompt } from "./util/config.js";
|
|
|
13
13
|
import { initWorkspace } from "./util/workspace.js";
|
|
14
14
|
import { loadSessions, resetSession } from "./storage/sessions.js";
|
|
15
15
|
import { loadChatSettings } from "./storage/chat-settings.js";
|
|
16
|
-
import { loadCronJobs } from "./storage/cron
|
|
17
|
-
import { loadTriggers } from "./storage/
|
|
16
|
+
import { loadCronJobs } from "./storage/cron.js";
|
|
17
|
+
import { loadTriggers } from "./storage/triggers.js";
|
|
18
18
|
import { clearHistory, loadHistory } from "./storage/history.js";
|
|
19
19
|
import { loadMediaIndex } from "./storage/media-index.js";
|
|
20
20
|
import { cleanupOldLogs } from "./storage/daily-log.js";
|
|
@@ -568,9 +568,12 @@ export async function initBackendAndDispatcher(
|
|
|
568
568
|
const { crossSendHandlers } =
|
|
569
569
|
await import("./core/engine/gateway-actions/cross-send.js");
|
|
570
570
|
void deliverPendingProvisionReport(async (frontend, target, text) => {
|
|
571
|
+
// Chat-free: the same "no chat" sentinels handleChatFreeAction passes.
|
|
571
572
|
const result = await crossSendHandlers.send_via(
|
|
572
573
|
{ frontend, target, text },
|
|
573
574
|
0,
|
|
575
|
+
undefined,
|
|
576
|
+
"0",
|
|
574
577
|
);
|
|
575
578
|
return Boolean(result && (result as { ok?: unknown }).ok === true);
|
|
576
579
|
});
|
package/src/cli/skill.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* `talon skill` — install / list / enable / disable / remove SKILL.md
|
|
3
3
|
* workflow bundles.
|
|
4
4
|
*
|
|
5
|
-
* All lifecycle logic lives in storage/
|
|
5
|
+
* All lifecycle logic lives in storage/skills.ts; this module only
|
|
6
6
|
* resolves install sources and renders outcomes. Installs accept a local
|
|
7
7
|
* folder, a git URL, or `owner/repo[/subpath]` (so `talon skill install
|
|
8
8
|
* anthropics/skills/skills/pdf` works). A source folder either IS a skill
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
listSkills,
|
|
23
23
|
setSkillEnabled,
|
|
24
24
|
type Skill,
|
|
25
|
-
} from "../storage/
|
|
25
|
+
} from "../storage/skills.js";
|
|
26
26
|
import { cloneShallow, resolveSource } from "./install-sources.js";
|
|
27
27
|
|
|
28
28
|
const USAGE = [
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
type CatchupPolicy,
|
|
19
19
|
type CronJob,
|
|
20
20
|
type CronJobType,
|
|
21
|
-
} from "../../storage/cron
|
|
21
|
+
} from "../../storage/cron.js";
|
|
22
22
|
|
|
23
23
|
/** The scheduler ticks once a minute, so sub-minute intervals are meaningless. */
|
|
24
24
|
const MIN_INTERVAL_SECONDS = 60;
|
|
@@ -31,9 +31,10 @@ import {
|
|
|
31
31
|
describeSchedule,
|
|
32
32
|
type CronJob,
|
|
33
33
|
type CronRunOutcome,
|
|
34
|
-
} from "../../storage/cron
|
|
34
|
+
} from "../../storage/cron.js";
|
|
35
35
|
import { appendDailyLog } from "../../storage/daily-log.js";
|
|
36
36
|
import { log, logError, logWarn } from "../../util/log.js";
|
|
37
|
+
import { numericChatIdFor } from "../../util/chat-id.js";
|
|
37
38
|
import { runJobOneShot } from "./job-oneshot.js";
|
|
38
39
|
import {
|
|
39
40
|
jobAllowsRun,
|
|
@@ -464,10 +465,7 @@ const CRON_JOB_TIMEOUT_MS = 10 * 60_000; // 10-minute max per job
|
|
|
464
465
|
export async function executeJob(job: CronJob): Promise<ExecuteJobResult> {
|
|
465
466
|
if (!deps) return { status: "skipped", reason: "cron is not initialised" };
|
|
466
467
|
|
|
467
|
-
const numericChatId =
|
|
468
|
-
if (!Number.isFinite(numericChatId)) {
|
|
469
|
-
throw new Error(`Invalid chatId for job "${job.name}": ${job.chatId}`);
|
|
470
|
-
}
|
|
468
|
+
const numericChatId = numericChatIdFor(job.chatId);
|
|
471
469
|
|
|
472
470
|
if (job.type === "message") {
|
|
473
471
|
await deps.sendMessage(numericChatId, job.content, job.chatId);
|
|
@@ -11,7 +11,7 @@ import { logError, logWarn } from "../../../util/log.js";
|
|
|
11
11
|
import { toYMD } from "../../../util/time.js";
|
|
12
12
|
import { getDefaultModel } from "../../models/catalog.js";
|
|
13
13
|
import { loadSystemTemplate } from "../../prompt/templates.js";
|
|
14
|
-
import { formatGoal, getOpenGoals } from "../../../storage/
|
|
14
|
+
import { formatGoal, getOpenGoals } from "../../../storage/goals.js";
|
|
15
15
|
import { taskTable, type TaskHandle } from "../../tasks/index.js";
|
|
16
16
|
import type { Backend } from "../../agent-runtime/capabilities.js";
|
|
17
17
|
import type { OneShotAgentParams } from "../../types.js";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { spawnSync } from "node:child_process";
|
|
8
|
-
import type { Trigger } from "../../../storage/
|
|
8
|
+
import type { Trigger } from "../../../storage/triggers.js";
|
|
9
9
|
import { selfInvocation } from "../../../util/mcp-launcher.js";
|
|
10
10
|
import { LUA_RUN_SUBCOMMAND } from "../../scripting/lua-runner.js";
|
|
11
11
|
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
SHUTDOWN_KILL_ERROR,
|
|
12
12
|
type Trigger,
|
|
13
13
|
type TriggerStatus,
|
|
14
|
-
} from "../../../storage/
|
|
14
|
+
} from "../../../storage/triggers.js";
|
|
15
15
|
import { log, logDebug, logError } from "../../../util/log.js";
|
|
16
16
|
import { appendDailyLog } from "../../../storage/daily-log.js";
|
|
17
17
|
import {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
RESTART_KILL_ERROR,
|
|
12
12
|
SHUTDOWN_KILL_ERROR,
|
|
13
13
|
type Trigger,
|
|
14
|
-
} from "../../../storage/
|
|
14
|
+
} from "../../../storage/triggers.js";
|
|
15
15
|
import { log, logError } from "../../../util/log.js";
|
|
16
16
|
import { depsHolder } from "./state.js";
|
|
17
17
|
import { fireWake } from "./output.js";
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
getTrigger,
|
|
12
12
|
updateTrigger,
|
|
13
13
|
type Trigger,
|
|
14
|
-
} from "../../../storage/
|
|
14
|
+
} from "../../../storage/triggers.js";
|
|
15
15
|
import { log, logError, logWarn } from "../../../util/log.js";
|
|
16
16
|
import { spawnWarden, type WardenExitEvent } from "../../../native/warden.js";
|
|
17
17
|
import {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
generateCronId,
|
|
12
12
|
describeSchedule,
|
|
13
13
|
nextRunAt,
|
|
14
|
-
} from "../../../storage/cron
|
|
14
|
+
} from "../../../storage/cron.js";
|
|
15
15
|
import { runJobNow } from "../../background/cron.js";
|
|
16
16
|
import { parseCronSpec } from "../../background/cron-spec.js";
|
|
17
17
|
import { log } from "../../../util/log.js";
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
acquireBackendInstance,
|
|
23
23
|
isModelValidForBackend,
|
|
24
24
|
} from "../backend-controller/index.js";
|
|
25
|
-
import { validateJobModelOverride } from "./
|
|
25
|
+
import { validateJobModelOverride } from "./validation.js";
|
|
26
26
|
import type { SharedActionHandlers } from "./types.js";
|
|
27
27
|
|
|
28
28
|
/**
|
|
@@ -33,19 +33,19 @@ import type { SharedActionHandlers } from "./types.js";
|
|
|
33
33
|
* Returns an error string, or null when valid.
|
|
34
34
|
*/
|
|
35
35
|
async function validateCronModelOverride(
|
|
36
|
-
|
|
36
|
+
chatKey: string,
|
|
37
37
|
model?: string,
|
|
38
38
|
provider?: string,
|
|
39
39
|
): Promise<string | null> {
|
|
40
|
-
const chatBackendId = getBackendIdForChat(
|
|
40
|
+
const chatBackendId = getBackendIdForChat(chatKey);
|
|
41
41
|
if (!provider || provider === chatBackendId) {
|
|
42
42
|
const capabilityErr = validateCronBackgroundCapability(
|
|
43
43
|
chatBackendId,
|
|
44
|
-
getBackendForChat(
|
|
44
|
+
getBackendForChat(chatKey),
|
|
45
45
|
);
|
|
46
46
|
if (capabilityErr) return capabilityErr;
|
|
47
47
|
if (!model) return null;
|
|
48
|
-
return validateJobModelOverride(
|
|
48
|
+
return validateJobModelOverride(chatKey, model);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
let acquired: Awaited<ReturnType<typeof acquireBackendInstance>> | null =
|
|
@@ -85,7 +85,7 @@ function validateCronBackgroundCapability(
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export const cronHandlers: SharedActionHandlers = {
|
|
88
|
-
create_cron_job: async (body, chatId) => {
|
|
88
|
+
create_cron_job: async (body, chatId, _backend, chatKey) => {
|
|
89
89
|
const parsed = parseCronSpec(body);
|
|
90
90
|
if (!parsed.ok) return { ok: false, error: parsed.error };
|
|
91
91
|
const spec = parsed.effective;
|
|
@@ -103,7 +103,7 @@ export const cronHandlers: SharedActionHandlers = {
|
|
|
103
103
|
// backend is rejected here instead of silently failing at fire time.
|
|
104
104
|
if (jobType === "query") {
|
|
105
105
|
const modelErr = await validateCronModelOverride(
|
|
106
|
-
|
|
106
|
+
chatKey,
|
|
107
107
|
spec.model,
|
|
108
108
|
spec.provider,
|
|
109
109
|
);
|
|
@@ -114,7 +114,7 @@ export const cronHandlers: SharedActionHandlers = {
|
|
|
114
114
|
addCronJob({
|
|
115
115
|
...spec,
|
|
116
116
|
id,
|
|
117
|
-
chatId:
|
|
117
|
+
chatId: chatKey,
|
|
118
118
|
enabled: true,
|
|
119
119
|
createdAt: Date.now(),
|
|
120
120
|
runCount: 0,
|
|
@@ -147,8 +147,8 @@ export const cronHandlers: SharedActionHandlers = {
|
|
|
147
147
|
};
|
|
148
148
|
},
|
|
149
149
|
|
|
150
|
-
list_cron_jobs: (body, chatId) => {
|
|
151
|
-
const jobs = getCronJobsForChat(
|
|
150
|
+
list_cron_jobs: (body, chatId, _backend, chatKey) => {
|
|
151
|
+
const jobs = getCronJobsForChat(chatKey);
|
|
152
152
|
if (jobs.length === 0)
|
|
153
153
|
return { ok: true, text: "No cron jobs in this chat." };
|
|
154
154
|
const fmt = (ms: number) =>
|
|
@@ -191,12 +191,12 @@ export const cronHandlers: SharedActionHandlers = {
|
|
|
191
191
|
};
|
|
192
192
|
},
|
|
193
193
|
|
|
194
|
-
edit_cron_job: async (body, chatId) => {
|
|
194
|
+
edit_cron_job: async (body, chatId, _backend, chatKey) => {
|
|
195
195
|
const jobId = String(body.job_id ?? "");
|
|
196
196
|
if (!jobId) return { ok: false, error: "Missing job_id" };
|
|
197
197
|
const job = getCronJob(jobId);
|
|
198
198
|
if (!job) return { ok: false, error: `Job ${jobId} not found` };
|
|
199
|
-
if (job.chatId !==
|
|
199
|
+
if (job.chatId !== chatKey)
|
|
200
200
|
return { ok: false, error: "Job belongs to a different chat" };
|
|
201
201
|
|
|
202
202
|
const parsed = parseCronSpec(body, job);
|
|
@@ -210,7 +210,7 @@ export const cronHandlers: SharedActionHandlers = {
|
|
|
210
210
|
("model" in updates || "provider" in updates || "type" in updates)
|
|
211
211
|
) {
|
|
212
212
|
const modelErr = await validateCronModelOverride(
|
|
213
|
-
|
|
213
|
+
chatKey,
|
|
214
214
|
effective.model,
|
|
215
215
|
effective.provider,
|
|
216
216
|
);
|
|
@@ -233,12 +233,12 @@ export const cronHandlers: SharedActionHandlers = {
|
|
|
233
233
|
};
|
|
234
234
|
},
|
|
235
235
|
|
|
236
|
-
run_cron_job: async (body, chatId) => {
|
|
236
|
+
run_cron_job: async (body, chatId, _backend, chatKey) => {
|
|
237
237
|
const jobId = String(body.job_id ?? "");
|
|
238
238
|
if (!jobId) return { ok: false, error: "Missing job_id" };
|
|
239
239
|
const job = getCronJob(jobId);
|
|
240
240
|
if (!job) return { ok: false, error: `Job ${jobId} not found` };
|
|
241
|
-
if (job.chatId !==
|
|
241
|
+
if (job.chatId !== chatKey)
|
|
242
242
|
return { ok: false, error: "Job belongs to a different chat" };
|
|
243
243
|
const result = await runJobNow(jobId);
|
|
244
244
|
if (!result.ok) return { ok: false, error: result.error ?? "Run failed" };
|
|
@@ -246,12 +246,12 @@ export const cronHandlers: SharedActionHandlers = {
|
|
|
246
246
|
return { ok: true, text: `Ran job "${job.name}" (${jobId}) now.` };
|
|
247
247
|
},
|
|
248
248
|
|
|
249
|
-
delete_cron_job: (body, chatId) => {
|
|
249
|
+
delete_cron_job: (body, chatId, _backend, chatKey) => {
|
|
250
250
|
const jobId = String(body.job_id ?? "");
|
|
251
251
|
if (!jobId) return { ok: false, error: "Missing job_id" };
|
|
252
252
|
const job = getCronJob(jobId);
|
|
253
253
|
if (!job) return { ok: false, error: `Job ${jobId} not found` };
|
|
254
|
-
if (job.chatId !==
|
|
254
|
+
if (job.chatId !== chatKey)
|
|
255
255
|
return { ok: false, error: "Job belongs to a different chat" };
|
|
256
256
|
deleteCronJob(jobId);
|
|
257
257
|
return { ok: true, text: `Deleted cron job "${job.name}" (${jobId})` };
|
|
@@ -20,13 +20,13 @@ import {
|
|
|
20
20
|
OPEN_GOAL_STATUSES,
|
|
21
21
|
type Goal,
|
|
22
22
|
type GoalStatus,
|
|
23
|
-
} from "../../../storage/
|
|
23
|
+
} from "../../../storage/goals.js";
|
|
24
24
|
import { log } from "../../../util/log.js";
|
|
25
|
-
import { parseDueDate } from "./
|
|
25
|
+
import { parseDueDate } from "./validation.js";
|
|
26
26
|
import type { SharedActionHandlers } from "./types.js";
|
|
27
27
|
|
|
28
28
|
export const goalHandlers: SharedActionHandlers = {
|
|
29
|
-
add_goal: (body, chatId) => {
|
|
29
|
+
add_goal: (body, chatId, _backend, chatKey) => {
|
|
30
30
|
const title = String(body.title ?? "").trim();
|
|
31
31
|
const description = body.description ? String(body.description) : undefined;
|
|
32
32
|
const priority = body.priority ?? "normal";
|
|
@@ -48,7 +48,7 @@ export const goalHandlers: SharedActionHandlers = {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const chatIdStr =
|
|
51
|
+
const chatIdStr = chatKey;
|
|
52
52
|
// No open-goal cap: goals grow freely. The heartbeat re-reads every open
|
|
53
53
|
// goal, so keep them tidy by closing finished ones (completed/abandoned).
|
|
54
54
|
|
|
@@ -75,10 +75,10 @@ export const goalHandlers: SharedActionHandlers = {
|
|
|
75
75
|
};
|
|
76
76
|
},
|
|
77
77
|
|
|
78
|
-
list_goals: (body, chatId) => {
|
|
78
|
+
list_goals: (body, chatId, _backend, chatKey) => {
|
|
79
79
|
const includeClosed = body.include_closed === true;
|
|
80
80
|
const goals = getGoalsForChat(
|
|
81
|
-
|
|
81
|
+
chatKey,
|
|
82
82
|
includeClosed ? GOAL_STATUSES : OPEN_GOAL_STATUSES,
|
|
83
83
|
);
|
|
84
84
|
if (goals.length === 0)
|
|
@@ -94,12 +94,12 @@ export const goalHandlers: SharedActionHandlers = {
|
|
|
94
94
|
};
|
|
95
95
|
},
|
|
96
96
|
|
|
97
|
-
update_goal: (body, chatId) => {
|
|
97
|
+
update_goal: (body, chatId, _backend, chatKey) => {
|
|
98
98
|
const goalId = String(body.goal_id ?? "");
|
|
99
99
|
if (!goalId) return { ok: false, error: "Missing goal_id" };
|
|
100
100
|
const goal = getGoal(goalId);
|
|
101
101
|
if (!goal) return { ok: false, error: `Goal ${goalId} not found` };
|
|
102
|
-
if (goal.chatId !==
|
|
102
|
+
if (goal.chatId !== chatKey)
|
|
103
103
|
return { ok: false, error: "Goal belongs to a different chat" };
|
|
104
104
|
|
|
105
105
|
const updates: Parameters<typeof updateGoal>[1] = {};
|
|
@@ -158,12 +158,12 @@ export const goalHandlers: SharedActionHandlers = {
|
|
|
158
158
|
};
|
|
159
159
|
},
|
|
160
160
|
|
|
161
|
-
delete_goal: (body, chatId) => {
|
|
161
|
+
delete_goal: (body, chatId, _backend, chatKey) => {
|
|
162
162
|
const goalId = String(body.goal_id ?? "");
|
|
163
163
|
if (!goalId) return { ok: false, error: "Missing goal_id" };
|
|
164
164
|
const goal = getGoal(goalId);
|
|
165
165
|
if (!goal) return { ok: false, error: `Goal ${goalId} not found` };
|
|
166
|
-
if (goal.chatId !==
|
|
166
|
+
if (goal.chatId !== chatKey)
|
|
167
167
|
return { ok: false, error: "Goal belongs to a different chat" };
|
|
168
168
|
deleteGoal(goalId);
|
|
169
169
|
return { ok: true, text: `Deleted goal "${goal.title}" (${goalId})` };
|
|
@@ -15,9 +15,9 @@ import { formatMediaIndex } from "../../../storage/media-index.js";
|
|
|
15
15
|
import type { SharedActionHandlers } from "./types.js";
|
|
16
16
|
|
|
17
17
|
export const historyHandlers: SharedActionHandlers = {
|
|
18
|
-
read_history: (body, chatId) => {
|
|
18
|
+
read_history: (body, chatId, _backend, chatKey) => {
|
|
19
19
|
const limit = Math.min(100, Number(body.limit ?? 30));
|
|
20
|
-
const cid =
|
|
20
|
+
const cid = chatKey;
|
|
21
21
|
// The tool schema advertises `offset_id` and `before` for paging back;
|
|
22
22
|
// this fallback used to ignore both and hand back the same newest
|
|
23
23
|
// window no matter what the model asked, so "go further back" was a
|
|
@@ -36,7 +36,7 @@ export const historyHandlers: SharedActionHandlers = {
|
|
|
36
36
|
return { ok: true, text: getRecentFormatted(cid, limit) };
|
|
37
37
|
},
|
|
38
38
|
|
|
39
|
-
search_history: (body, chatId) => {
|
|
39
|
+
search_history: (body, chatId, _backend, chatKey) => {
|
|
40
40
|
const limit = Math.min(100, Number(body.limit ?? 20));
|
|
41
41
|
// Optional ISO date bounds; an unparseable one is ignored rather
|
|
42
42
|
// than silently narrowing the search to nothing.
|
|
@@ -48,35 +48,28 @@ export const historyHandlers: SharedActionHandlers = {
|
|
|
48
48
|
};
|
|
49
49
|
return {
|
|
50
50
|
ok: true,
|
|
51
|
-
text: searchHistory(
|
|
51
|
+
text: searchHistory(chatKey, String(body.query ?? ""), limit, {
|
|
52
52
|
after: bound(body.after),
|
|
53
53
|
before: bound(body.before),
|
|
54
54
|
}),
|
|
55
55
|
};
|
|
56
56
|
},
|
|
57
57
|
|
|
58
|
-
get_user_messages: (body, chatId) => {
|
|
58
|
+
get_user_messages: (body, chatId, _backend, chatKey) => {
|
|
59
59
|
const limit = Math.min(50, Number(body.limit ?? 20));
|
|
60
60
|
return {
|
|
61
61
|
ok: true,
|
|
62
|
-
text: getMessagesByUser(
|
|
63
|
-
String(chatId),
|
|
64
|
-
String(body.user_name ?? ""),
|
|
65
|
-
limit,
|
|
66
|
-
),
|
|
62
|
+
text: getMessagesByUser(chatKey, String(body.user_name ?? ""), limit),
|
|
67
63
|
};
|
|
68
64
|
},
|
|
69
65
|
|
|
70
|
-
list_known_users: (body, chatId) => ({
|
|
66
|
+
list_known_users: (body, chatId, _backend, chatKey) => ({
|
|
71
67
|
ok: true,
|
|
72
|
-
text: getKnownUsers(
|
|
68
|
+
text: getKnownUsers(chatKey),
|
|
73
69
|
}),
|
|
74
70
|
|
|
75
|
-
list_media: (body, chatId) => ({
|
|
71
|
+
list_media: (body, chatId, _backend, chatKey) => ({
|
|
76
72
|
ok: true,
|
|
77
|
-
text: formatMediaIndex(
|
|
78
|
-
String(chatId),
|
|
79
|
-
Math.min(20, Number(body.limit ?? 10)),
|
|
80
|
-
),
|
|
73
|
+
text: formatMediaIndex(chatKey, Math.min(20, Number(body.limit ?? 10))),
|
|
81
74
|
}),
|
|
82
75
|
};
|
|
@@ -72,15 +72,22 @@ const chatFreeActions: ReadonlySet<string> = new Set([
|
|
|
72
72
|
...whatsappAccountChatFreeActions,
|
|
73
73
|
]);
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* `chatKey` is the chat's canonical string id (see `SharedActionHandler`).
|
|
77
|
+
* It defaults to `String(chatId)`, which is exact for Telegram and for the
|
|
78
|
+
* numeric-only callers (tests, chat-free dispatch); the gateway passes the
|
|
79
|
+
* real string id it holds for the active turn.
|
|
80
|
+
*/
|
|
75
81
|
export async function handleSharedAction(
|
|
76
82
|
body: Record<string, unknown>,
|
|
77
83
|
chatId: number,
|
|
78
84
|
backend?: Backend | null,
|
|
85
|
+
chatKey: string = String(chatId),
|
|
79
86
|
): Promise<ActionResult | null> {
|
|
80
87
|
const action = body.action as string;
|
|
81
88
|
const handler = handlers[action];
|
|
82
89
|
if (!handler) return null; // not a shared action — delegate to frontend
|
|
83
|
-
return handler(body, chatId, backend);
|
|
90
|
+
return handler(body, chatId, backend, chatKey);
|
|
84
91
|
}
|
|
85
92
|
|
|
86
93
|
/**
|
|
@@ -104,5 +111,5 @@ export async function handleChatFreeAction(
|
|
|
104
111
|
if (!isChatFreeAction(action)) return null;
|
|
105
112
|
const handler = handlers[action];
|
|
106
113
|
if (!handler) return null;
|
|
107
|
-
return handler(body, 0);
|
|
114
|
+
return handler(body, 0, undefined, "0");
|
|
108
115
|
}
|
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
import type { SharedActionHandlers } from "./types.js";
|
|
17
17
|
|
|
18
18
|
export const modelHandlers: SharedActionHandlers = {
|
|
19
|
-
list_models: async (body, chatId) => {
|
|
20
|
-
const chatIdStr =
|
|
19
|
+
list_models: async (body, chatId, _backend, chatKey) => {
|
|
20
|
+
const chatIdStr = chatKey;
|
|
21
21
|
const currentId = getBackendIdForChat(chatIdStr);
|
|
22
22
|
const requested = body.backend ? String(body.backend).trim() : "";
|
|
23
23
|
const targetId = requested || currentId;
|
|
@@ -100,8 +100,8 @@ export const modelHandlers: SharedActionHandlers = {
|
|
|
100
100
|
|
|
101
101
|
// Account-level, so it falls back to the pooled Claude backend when
|
|
102
102
|
// another provider is serving this chat.
|
|
103
|
-
plan_usage: async (_body, chatId) => {
|
|
104
|
-
const current = getPooledBackend(getBackendIdForChat(
|
|
103
|
+
plan_usage: async (_body, chatId, _backend, chatKey) => {
|
|
104
|
+
const current = getPooledBackend(getBackendIdForChat(chatKey));
|
|
105
105
|
const source = current?.usage?.getPlanUsage
|
|
106
106
|
? current
|
|
107
107
|
: getPooledBackend("claude");
|
|
@@ -131,8 +131,8 @@ export const modelHandlers: SharedActionHandlers = {
|
|
|
131
131
|
};
|
|
132
132
|
},
|
|
133
133
|
|
|
134
|
-
list_backends: (body, chatId) => {
|
|
135
|
-
const currentId = getBackendIdForChat(
|
|
134
|
+
list_backends: (body, chatId, _backend, chatKey) => {
|
|
135
|
+
const currentId = getBackendIdForChat(chatKey);
|
|
136
136
|
const backends = getAvailableBackends().map((b) => ({
|
|
137
137
|
id: b.id,
|
|
138
138
|
label: b.label,
|
|
@@ -43,7 +43,7 @@ export async function performPluginReload(
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
export const pluginHandlers: SharedActionHandlers = {
|
|
46
|
-
reload_plugins: async (body, chatId, backend) => {
|
|
46
|
+
reload_plugins: async (body, chatId, backend, chatKey) => {
|
|
47
47
|
try {
|
|
48
48
|
const { names } = await performPluginReload(backend);
|
|
49
49
|
|
|
@@ -52,13 +52,7 @@ export const pluginHandlers: SharedActionHandlers = {
|
|
|
52
52
|
let mcpInfo = "";
|
|
53
53
|
if (backend?.tools?.refreshTools) {
|
|
54
54
|
try {
|
|
55
|
-
|
|
56
|
-
// non-numeric IDs, e.g. Teams/terminal) over the numeric context ID.
|
|
57
|
-
const refreshChatId =
|
|
58
|
-
typeof body._chatId === "string" && body._chatId.length > 0
|
|
59
|
-
? body._chatId
|
|
60
|
-
: String(chatId);
|
|
61
|
-
const result = await backend.tools.refreshTools(refreshChatId);
|
|
55
|
+
const result = await backend.tools.refreshTools(chatKey);
|
|
62
56
|
if (result) {
|
|
63
57
|
const parts: string[] = [];
|
|
64
58
|
if (result.added.length > 0)
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
validateSkillBody,
|
|
16
16
|
validateSkillDescription,
|
|
17
17
|
validateSkillName,
|
|
18
|
-
} from "../../../storage/
|
|
18
|
+
} from "../../../storage/skills.js";
|
|
19
19
|
import { log } from "../../../util/log.js";
|
|
20
20
|
import type { SharedActionHandlers } from "./types.js";
|
|
21
21
|
|