@rubytech/taskmaster 1.16.3 → 1.17.4
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/agents/tools/logs-read-tool.js +9 -0
- package/dist/agents/tools/memory-tool.js +1 -0
- package/dist/agents/workspace-migrations.js +61 -0
- package/dist/auto-reply/group-activation.js +2 -0
- package/dist/auto-reply/reply/commands-session.js +28 -11
- package/dist/build-info.json +3 -3
- package/dist/config/agent-tools-reconcile.js +58 -0
- package/dist/config/group-policy.js +16 -0
- package/dist/config/zod-schema.providers-whatsapp.js +2 -0
- package/dist/control-ui/assets/index-XqRo9tNW.css +1 -0
- package/dist/control-ui/assets/{index-Bd75cI7J.js → index-koe4eKhk.js} +526 -493
- package/dist/control-ui/assets/index-koe4eKhk.js.map +1 -0
- package/dist/control-ui/index.html +2 -2
- package/dist/cron/preloaded.js +27 -23
- package/dist/cron/service/timer.js +5 -1
- package/dist/gateway/protocol/index.js +7 -2
- package/dist/gateway/protocol/schema/logs-chat.js +6 -0
- package/dist/gateway/protocol/schema/protocol-schemas.js +6 -0
- package/dist/gateway/protocol/schema/sessions-transcript.js +1 -0
- package/dist/gateway/protocol/schema/sessions.js +6 -1
- package/dist/gateway/protocol/schema/whatsapp.js +24 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/public-chat/session-token.js +52 -0
- package/dist/gateway/public-chat-api.js +40 -13
- package/dist/gateway/server-methods/apikeys.js +2 -0
- package/dist/gateway/server-methods/logs.js +17 -1
- package/dist/gateway/server-methods/public-chat.js +5 -0
- package/dist/gateway/server-methods/sessions-transcript.js +30 -6
- package/dist/gateway/server-methods/whatsapp-conversations.js +387 -0
- package/dist/gateway/server-methods-list.js +6 -0
- package/dist/gateway/server-methods.js +7 -0
- package/dist/gateway/server.impl.js +19 -2
- package/dist/gateway/sessions-patch.js +1 -1
- package/dist/hooks/bundled/ride-dispatch/HOOK.md +7 -6
- package/dist/hooks/bundled/ride-dispatch/handler.js +98 -39
- package/dist/memory/manager.js +3 -3
- package/dist/tui/tui-command-handlers.js +1 -1
- package/dist/web/auto-reply/monitor/group-activation.js +12 -10
- package/dist/web/auto-reply/monitor/group-gating.js +23 -2
- package/dist/web/auto-reply/monitor/on-message.js +27 -5
- package/dist/web/auto-reply/monitor/process-message.js +64 -53
- package/dist/web/inbound/monitor.js +30 -0
- package/extensions/whatsapp/src/channel.ts +1 -1
- package/package.json +1 -1
- package/skills/log-review/SKILL.md +17 -4
- package/skills/log-review/references/review-protocol.md +4 -4
- package/taskmaster-docs/USER-GUIDE.md +14 -0
- package/templates/beagle-zanzibar/agents/admin/AGENTS.md +16 -8
- package/templates/beagle-zanzibar/agents/public/AGENTS.md +10 -5
- package/dist/control-ui/assets/index-Bd75cI7J.js.map +0 -1
- package/dist/control-ui/assets/index-BkymP95Y.css +0 -1
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<title>Taskmaster Control</title>
|
|
7
7
|
<meta name="color-scheme" content="dark light" />
|
|
8
8
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
|
9
|
-
<script type="module" crossorigin src="./assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
9
|
+
<script type="module" crossorigin src="./assets/index-koe4eKhk.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="./assets/index-XqRo9tNW.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<taskmaster-app></taskmaster-app>
|
package/dist/cron/preloaded.js
CHANGED
|
@@ -20,11 +20,11 @@ export async function saveSeedTracker(trackerPath, tracker) {
|
|
|
20
20
|
const json = JSON.stringify(tracker, null, 2);
|
|
21
21
|
await fs.promises.writeFile(trackerPath, json, "utf-8");
|
|
22
22
|
}
|
|
23
|
-
export function isTemplateSeeded(tracker, templateId) {
|
|
24
|
-
return tracker.seeded.some((e) => e.templateId === templateId);
|
|
23
|
+
export function isTemplateSeeded(tracker, templateId, accountId) {
|
|
24
|
+
return tracker.seeded.some((e) => e.templateId === templateId && e.accountId === accountId);
|
|
25
25
|
}
|
|
26
|
-
export function markTemplateSeeded(tracker, templateId, jobId) {
|
|
27
|
-
tracker.seeded.push({ templateId, jobId, seededAtMs: Date.now() });
|
|
26
|
+
export function markTemplateSeeded(tracker, templateId, jobId, accountId) {
|
|
27
|
+
tracker.seeded.push({ templateId, accountId, jobId, seededAtMs: Date.now() });
|
|
28
28
|
}
|
|
29
29
|
// ---------------------------------------------------------------------------
|
|
30
30
|
// Template loader — scan bundled skills for cron-template.json files
|
|
@@ -63,30 +63,34 @@ export function loadCronTemplatesFromBundledSkills(bundledSkillsDir) {
|
|
|
63
63
|
// Seeding function — create cron jobs from un-seeded templates
|
|
64
64
|
// ---------------------------------------------------------------------------
|
|
65
65
|
export async function seedPreloadedCronJobs(params) {
|
|
66
|
-
const { bundledSkillsDir, trackerPath, cronService,
|
|
66
|
+
const { bundledSkillsDir, trackerPath, cronService, accountIds } = params;
|
|
67
|
+
if (accountIds.length === 0)
|
|
68
|
+
return 0;
|
|
67
69
|
const templates = loadCronTemplatesFromBundledSkills(bundledSkillsDir);
|
|
68
70
|
if (templates.length === 0)
|
|
69
71
|
return 0;
|
|
70
72
|
const tracker = await loadSeedTracker(trackerPath);
|
|
71
73
|
let seeded = 0;
|
|
72
|
-
for (const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
74
|
+
for (const accountId of accountIds) {
|
|
75
|
+
for (const template of templates) {
|
|
76
|
+
if (isTemplateSeeded(tracker, template.templateId, accountId))
|
|
77
|
+
continue;
|
|
78
|
+
const job = await cronService.add({
|
|
79
|
+
name: template.name,
|
|
80
|
+
description: template.description,
|
|
81
|
+
enabled: template.enabled,
|
|
82
|
+
agentId: template.agentId,
|
|
83
|
+
accountId,
|
|
84
|
+
schedule: template.schedule,
|
|
85
|
+
sessionTarget: template.sessionTarget,
|
|
86
|
+
wakeMode: template.wakeMode,
|
|
87
|
+
payload: template.payload,
|
|
88
|
+
isolation: template.isolation,
|
|
89
|
+
});
|
|
90
|
+
markTemplateSeeded(tracker, template.templateId, job.id, accountId);
|
|
91
|
+
await saveSeedTracker(trackerPath, tracker);
|
|
92
|
+
seeded++;
|
|
93
|
+
}
|
|
90
94
|
}
|
|
91
95
|
return seeded;
|
|
92
96
|
}
|
|
@@ -103,7 +103,11 @@ export async function executeJob(state, job, nowMs, opts) {
|
|
|
103
103
|
}
|
|
104
104
|
if (job.sessionTarget === "isolated") {
|
|
105
105
|
const prefix = job.isolation?.postToMainPrefix?.trim() || "Cron";
|
|
106
|
-
const
|
|
106
|
+
const configuredMode = job.isolation?.postToMainMode ?? "summary";
|
|
107
|
+
// When delivery was skipped (best-effort, no external channel configured),
|
|
108
|
+
// promote to "full" so the admin sees the actual report in their main chat
|
|
109
|
+
// instead of a delivery-error summary.
|
|
110
|
+
const mode = status === "skipped" && outputText ? "full" : configuredMode;
|
|
107
111
|
let body = (summary ?? err ?? status).trim();
|
|
108
112
|
if (mode === "full") {
|
|
109
113
|
// Prefer full agent output if available; fall back to summary.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import AjvPkg from "ajv";
|
|
2
|
-
import { AgentEventSchema, AgentIdentityParamsSchema, AgentIdentityResultSchema, AgentParamsSchema, AgentSummarySchema, AgentsListParamsSchema, AgentsListResultSchema, AgentWaitParamsSchema, ChannelsLogoutParamsSchema, ChannelsStatusParamsSchema, ChannelsStatusResultSchema, ChatAbortParamsSchema, ChatEventSchema, ChatHistoryParamsSchema, ChatInjectParamsSchema, ChatSendParamsSchema, ConfigApplyParamsSchema, ConfigGetParamsSchema, ConfigPatchParamsSchema, ConfigSchemaParamsSchema, ConfigSchemaResponseSchema, ConfigSetParamsSchema, ConnectParamsSchema, CronAddParamsSchema, CronJobSchema, CronListParamsSchema, CronRemoveParamsSchema, CronRunParamsSchema, CronRunsParamsSchema, CronStatusParamsSchema, CronUpdateParamsSchema, DevicePairApproveParamsSchema, DevicePairListParamsSchema, DevicePairRejectParamsSchema, DeviceTokenRevokeParamsSchema, DeviceTokenRotateParamsSchema, ExecApprovalsGetParamsSchema, ExecApprovalsNodeGetParamsSchema, ExecApprovalsNodeSetParamsSchema, ExecApprovalsSetParamsSchema, ExecApprovalRequestParamsSchema, ExecApprovalResolveParamsSchema, ErrorCodes, ErrorShapeSchema, EventFrameSchema, errorShape, GatewayFrameSchema, HelloOkSchema, LogsTailParamsSchema, LogsTailResultSchema, SessionsTranscriptParamsSchema, SessionsTranscriptResultSchema, ModelsListParamsSchema, NodeDescribeParamsSchema, NodeEventParamsSchema, NodeInvokeParamsSchema, NodeInvokeResultParamsSchema, NodeListParamsSchema, NodePairApproveParamsSchema, NodePairListParamsSchema, NodePairRejectParamsSchema, NodePairRequestParamsSchema, NodePairVerifyParamsSchema, NodeRenameParamsSchema, PollParamsSchema, PROTOCOL_VERSION, PresenceEntrySchema, ProtocolSchemas, RequestFrameSchema, ResponseFrameSchema, SendParamsSchema, SessionsCompactParamsSchema, SessionsDeleteParamsSchema, SessionsListParamsSchema, SessionsPatchParamsSchema, SessionsPreviewParamsSchema, SessionsResetParamsSchema, SessionsResolveParamsSchema, ShutdownEventSchema, SkillsBinsParamsSchema, SkillsCreateParamsSchema, SkillsDeleteParamsSchema, SkillsDeleteDraftParamsSchema, SkillsDraftsParamsSchema, SkillsSaveDraftParamsSchema, SkillsInstallParamsSchema, SkillsReadParamsSchema, SkillsStatusParamsSchema, SkillsUpdateParamsSchema, SnapshotSchema, StateVersionSchema, TalkModeParamsSchema, TickEventSchema, UpdateRunParamsSchema, WakeParamsSchema, WebLoginStartParamsSchema, WebLoginWaitParamsSchema, WizardCancelParamsSchema, WizardNextParamsSchema, WizardNextResultSchema, WizardStartParamsSchema, WizardStartResultSchema, WizardStatusParamsSchema, WizardStatusResultSchema, WizardStepSchema, } from "./schema.js";
|
|
2
|
+
import { AgentEventSchema, AgentIdentityParamsSchema, AgentIdentityResultSchema, AgentParamsSchema, AgentSummarySchema, AgentsListParamsSchema, AgentsListResultSchema, AgentWaitParamsSchema, ChannelsLogoutParamsSchema, ChannelsStatusParamsSchema, ChannelsStatusResultSchema, ChatAbortParamsSchema, ChatEventSchema, ChatHistoryParamsSchema, ChatInjectParamsSchema, ChatSendParamsSchema, ConfigApplyParamsSchema, ConfigGetParamsSchema, ConfigPatchParamsSchema, ConfigSchemaParamsSchema, ConfigSchemaResponseSchema, ConfigSetParamsSchema, ConnectParamsSchema, CronAddParamsSchema, CronJobSchema, CronListParamsSchema, CronRemoveParamsSchema, CronRunParamsSchema, CronRunsParamsSchema, CronStatusParamsSchema, CronUpdateParamsSchema, DevicePairApproveParamsSchema, DevicePairListParamsSchema, DevicePairRejectParamsSchema, DeviceTokenRevokeParamsSchema, DeviceTokenRotateParamsSchema, ExecApprovalsGetParamsSchema, ExecApprovalsNodeGetParamsSchema, ExecApprovalsNodeSetParamsSchema, ExecApprovalsSetParamsSchema, ExecApprovalRequestParamsSchema, ExecApprovalResolveParamsSchema, ErrorCodes, ErrorShapeSchema, EventFrameSchema, errorShape, GatewayFrameSchema, HelloOkSchema, LogsTailParamsSchema, LogsTailResultSchema, SessionsTranscriptParamsSchema, SessionsTranscriptResultSchema, ModelsListParamsSchema, NodeDescribeParamsSchema, NodeEventParamsSchema, NodeInvokeParamsSchema, NodeInvokeResultParamsSchema, NodeListParamsSchema, NodePairApproveParamsSchema, NodePairListParamsSchema, NodePairRejectParamsSchema, NodePairRequestParamsSchema, NodePairVerifyParamsSchema, NodeRenameParamsSchema, PollParamsSchema, PROTOCOL_VERSION, PresenceEntrySchema, ProtocolSchemas, RequestFrameSchema, ResponseFrameSchema, SendParamsSchema, SessionsCompactParamsSchema, SessionsDeleteParamsSchema, SessionsListParamsSchema, SessionsPatchParamsSchema, SessionsPreviewParamsSchema, SessionsResetParamsSchema, SessionsResolveParamsSchema, ShutdownEventSchema, SkillsBinsParamsSchema, SkillsCreateParamsSchema, SkillsDeleteParamsSchema, SkillsDeleteDraftParamsSchema, SkillsDraftsParamsSchema, SkillsSaveDraftParamsSchema, SkillsInstallParamsSchema, SkillsReadParamsSchema, SkillsStatusParamsSchema, SkillsUpdateParamsSchema, SnapshotSchema, StateVersionSchema, TalkModeParamsSchema, TickEventSchema, UpdateRunParamsSchema, WakeParamsSchema, WebLoginStartParamsSchema, WebLoginWaitParamsSchema, WizardCancelParamsSchema, WizardNextParamsSchema, WizardNextResultSchema, WhatsAppConversationsParamsSchema, WhatsAppGroupInfoParamsSchema, WhatsAppMessagesParamsSchema, WhatsAppSendMessageParamsSchema, WhatsAppSetActivationParamsSchema, WizardStartParamsSchema, WizardStartResultSchema, WizardStatusParamsSchema, WizardStatusResultSchema, WizardStepSchema, } from "./schema.js";
|
|
3
3
|
const ajv = new AjvPkg({
|
|
4
4
|
allErrors: true,
|
|
5
5
|
strict: false,
|
|
@@ -85,6 +85,11 @@ export const validateChatEvent = ajv.compile(ChatEventSchema);
|
|
|
85
85
|
export const validateUpdateRunParams = ajv.compile(UpdateRunParamsSchema);
|
|
86
86
|
export const validateWebLoginStartParams = ajv.compile(WebLoginStartParamsSchema);
|
|
87
87
|
export const validateWebLoginWaitParams = ajv.compile(WebLoginWaitParamsSchema);
|
|
88
|
+
export const validateWhatsAppConversationsParams = ajv.compile(WhatsAppConversationsParamsSchema);
|
|
89
|
+
export const validateWhatsAppMessagesParams = ajv.compile(WhatsAppMessagesParamsSchema);
|
|
90
|
+
export const validateWhatsAppGroupInfoParams = ajv.compile(WhatsAppGroupInfoParamsSchema);
|
|
91
|
+
export const validateWhatsAppSetActivationParams = ajv.compile(WhatsAppSetActivationParamsSchema);
|
|
92
|
+
export const validateWhatsAppSendMessageParams = ajv.compile(WhatsAppSendMessageParamsSchema);
|
|
88
93
|
export function formatValidationErrors(errors) {
|
|
89
94
|
if (!errors?.length)
|
|
90
95
|
return "unknown validation error";
|
|
@@ -113,4 +118,4 @@ export function formatValidationErrors(errors) {
|
|
|
113
118
|
}
|
|
114
119
|
return unique.join("; ");
|
|
115
120
|
}
|
|
116
|
-
export { ConnectParamsSchema, HelloOkSchema, RequestFrameSchema, ResponseFrameSchema, EventFrameSchema, GatewayFrameSchema, PresenceEntrySchema, SnapshotSchema, ErrorShapeSchema, StateVersionSchema, AgentEventSchema, ChatEventSchema, SendParamsSchema, PollParamsSchema, AgentParamsSchema, AgentIdentityParamsSchema, AgentIdentityResultSchema, WakeParamsSchema, NodePairRequestParamsSchema, NodePairListParamsSchema, NodePairApproveParamsSchema, NodePairRejectParamsSchema, NodePairVerifyParamsSchema, NodeListParamsSchema, NodeInvokeParamsSchema, SessionsListParamsSchema, SessionsPreviewParamsSchema, SessionsPatchParamsSchema, SessionsResetParamsSchema, SessionsDeleteParamsSchema, SessionsCompactParamsSchema, ConfigGetParamsSchema, ConfigSetParamsSchema, ConfigApplyParamsSchema, ConfigPatchParamsSchema, ConfigSchemaParamsSchema, ConfigSchemaResponseSchema, WizardStartParamsSchema, WizardNextParamsSchema, WizardCancelParamsSchema, WizardStatusParamsSchema, WizardStepSchema, WizardNextResultSchema, WizardStartResultSchema, WizardStatusResultSchema, ChannelsStatusParamsSchema, ChannelsStatusResultSchema, ChannelsLogoutParamsSchema, WebLoginStartParamsSchema, WebLoginWaitParamsSchema, AgentSummarySchema, AgentsListParamsSchema, AgentsListResultSchema, ModelsListParamsSchema, SkillsStatusParamsSchema, SkillsInstallParamsSchema, SkillsUpdateParamsSchema, CronJobSchema, CronListParamsSchema, CronStatusParamsSchema, CronAddParamsSchema, CronUpdateParamsSchema, CronRemoveParamsSchema, CronRunParamsSchema, CronRunsParamsSchema, LogsTailParamsSchema, LogsTailResultSchema, SessionsTranscriptParamsSchema, SessionsTranscriptResultSchema, ChatHistoryParamsSchema, ChatSendParamsSchema, ChatInjectParamsSchema, UpdateRunParamsSchema, TickEventSchema, ShutdownEventSchema, ProtocolSchemas, PROTOCOL_VERSION, ErrorCodes, errorShape, };
|
|
121
|
+
export { ConnectParamsSchema, HelloOkSchema, RequestFrameSchema, ResponseFrameSchema, EventFrameSchema, GatewayFrameSchema, PresenceEntrySchema, SnapshotSchema, ErrorShapeSchema, StateVersionSchema, AgentEventSchema, ChatEventSchema, SendParamsSchema, PollParamsSchema, AgentParamsSchema, AgentIdentityParamsSchema, AgentIdentityResultSchema, WakeParamsSchema, NodePairRequestParamsSchema, NodePairListParamsSchema, NodePairApproveParamsSchema, NodePairRejectParamsSchema, NodePairVerifyParamsSchema, NodeListParamsSchema, NodeInvokeParamsSchema, SessionsListParamsSchema, SessionsPreviewParamsSchema, SessionsPatchParamsSchema, SessionsResetParamsSchema, SessionsDeleteParamsSchema, SessionsCompactParamsSchema, ConfigGetParamsSchema, ConfigSetParamsSchema, ConfigApplyParamsSchema, ConfigPatchParamsSchema, ConfigSchemaParamsSchema, ConfigSchemaResponseSchema, WizardStartParamsSchema, WizardNextParamsSchema, WizardCancelParamsSchema, WizardStatusParamsSchema, WizardStepSchema, WizardNextResultSchema, WizardStartResultSchema, WizardStatusResultSchema, ChannelsStatusParamsSchema, ChannelsStatusResultSchema, ChannelsLogoutParamsSchema, WebLoginStartParamsSchema, WebLoginWaitParamsSchema, WhatsAppConversationsParamsSchema, WhatsAppGroupInfoParamsSchema, WhatsAppMessagesParamsSchema, WhatsAppSendMessageParamsSchema, WhatsAppSetActivationParamsSchema, AgentSummarySchema, AgentsListParamsSchema, AgentsListResultSchema, ModelsListParamsSchema, SkillsStatusParamsSchema, SkillsInstallParamsSchema, SkillsUpdateParamsSchema, CronJobSchema, CronListParamsSchema, CronStatusParamsSchema, CronAddParamsSchema, CronUpdateParamsSchema, CronRemoveParamsSchema, CronRunParamsSchema, CronRunsParamsSchema, LogsTailParamsSchema, LogsTailResultSchema, SessionsTranscriptParamsSchema, SessionsTranscriptResultSchema, ChatHistoryParamsSchema, ChatSendParamsSchema, ChatInjectParamsSchema, UpdateRunParamsSchema, TickEventSchema, ShutdownEventSchema, ProtocolSchemas, PROTOCOL_VERSION, ErrorCodes, errorShape, };
|
|
@@ -4,6 +4,12 @@ export const LogsTailParamsSchema = Type.Object({
|
|
|
4
4
|
cursor: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
5
5
|
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 5000 })),
|
|
6
6
|
maxBytes: Type.Optional(Type.Integer({ minimum: 1, maximum: 1_000_000 })),
|
|
7
|
+
minLevel: Type.Optional(Type.Union([
|
|
8
|
+
Type.Literal("fatal"),
|
|
9
|
+
Type.Literal("error"),
|
|
10
|
+
Type.Literal("warn"),
|
|
11
|
+
Type.Literal("info"),
|
|
12
|
+
])),
|
|
7
13
|
}, { additionalProperties: false });
|
|
8
14
|
export const LogsTailResultSchema = Type.Object({
|
|
9
15
|
file: NonEmptyString,
|
|
@@ -10,6 +10,7 @@ import { ChatAbortParamsSchema, ChatEventSchema, ChatHistoryParamsSchema, ChatIn
|
|
|
10
10
|
import { NodeDescribeParamsSchema, NodeEventParamsSchema, NodeInvokeParamsSchema, NodeInvokeResultParamsSchema, NodeInvokeRequestEventSchema, NodeListParamsSchema, NodePairApproveParamsSchema, NodePairListParamsSchema, NodePairRejectParamsSchema, NodePairRequestParamsSchema, NodePairVerifyParamsSchema, NodeRenameParamsSchema, } from "./nodes.js";
|
|
11
11
|
import { SessionsCompactParamsSchema, SessionsDeleteParamsSchema, SessionsListParamsSchema, SessionsPatchParamsSchema, SessionsPreviewParamsSchema, SessionsResetParamsSchema, SessionsResolveParamsSchema, } from "./sessions.js";
|
|
12
12
|
import { PresenceEntrySchema, SnapshotSchema, StateVersionSchema } from "./snapshot.js";
|
|
13
|
+
import { WhatsAppConversationsParamsSchema, WhatsAppGroupInfoParamsSchema, WhatsAppMessagesParamsSchema, WhatsAppSendMessageParamsSchema, WhatsAppSetActivationParamsSchema, } from "./whatsapp.js";
|
|
13
14
|
import { WizardCancelParamsSchema, WizardNextParamsSchema, WizardNextResultSchema, WizardStartParamsSchema, WizardStartResultSchema, WizardStatusParamsSchema, WizardStatusResultSchema, WizardStepSchema, } from "./wizard.js";
|
|
14
15
|
export const ProtocolSchemas = {
|
|
15
16
|
ConnectParams: ConnectParamsSchema,
|
|
@@ -117,6 +118,11 @@ export const ProtocolSchemas = {
|
|
|
117
118
|
ChatInjectParams: ChatInjectParamsSchema,
|
|
118
119
|
ChatEvent: ChatEventSchema,
|
|
119
120
|
UpdateRunParams: UpdateRunParamsSchema,
|
|
121
|
+
WhatsAppConversationsParams: WhatsAppConversationsParamsSchema,
|
|
122
|
+
WhatsAppMessagesParams: WhatsAppMessagesParamsSchema,
|
|
123
|
+
WhatsAppGroupInfoParams: WhatsAppGroupInfoParamsSchema,
|
|
124
|
+
WhatsAppSetActivationParams: WhatsAppSetActivationParamsSchema,
|
|
125
|
+
WhatsAppSendMessageParams: WhatsAppSendMessageParamsSchema,
|
|
120
126
|
TickEvent: TickEventSchema,
|
|
121
127
|
ShutdownEvent: ShutdownEventSchema,
|
|
122
128
|
};
|
|
@@ -3,6 +3,7 @@ export const SessionsTranscriptParamsSchema = Type.Object({
|
|
|
3
3
|
cursors: Type.Optional(Type.Record(Type.String(), Type.Integer({ minimum: 0 }))),
|
|
4
4
|
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 10_000 })),
|
|
5
5
|
agents: Type.Optional(Type.Array(Type.String())),
|
|
6
|
+
errorsOnly: Type.Optional(Type.Boolean()),
|
|
6
7
|
}, { additionalProperties: false });
|
|
7
8
|
export const SessionsTranscriptEntrySchema = Type.Object({
|
|
8
9
|
sessionId: Type.String(),
|
|
@@ -56,7 +56,12 @@ export const SessionsPatchParamsSchema = Type.Object({
|
|
|
56
56
|
model: Type.Optional(Type.Union([NonEmptyString, Type.Null()])),
|
|
57
57
|
spawnedBy: Type.Optional(Type.Union([NonEmptyString, Type.Null()])),
|
|
58
58
|
sendPolicy: Type.Optional(Type.Union([Type.Literal("allow"), Type.Literal("deny"), Type.Null()])),
|
|
59
|
-
groupActivation: Type.Optional(Type.Union([
|
|
59
|
+
groupActivation: Type.Optional(Type.Union([
|
|
60
|
+
Type.Literal("mention"),
|
|
61
|
+
Type.Literal("always"),
|
|
62
|
+
Type.Literal("off"),
|
|
63
|
+
Type.Null(),
|
|
64
|
+
])),
|
|
60
65
|
fillerEnabled: Type.Optional(Type.Union([Type.Boolean(), Type.Null()])),
|
|
61
66
|
}, { additionalProperties: false });
|
|
62
67
|
export const SessionsResetParamsSchema = Type.Object({ key: NonEmptyString }, { additionalProperties: false });
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { NonEmptyString } from "./primitives.js";
|
|
3
|
+
export const WhatsAppConversationsParamsSchema = Type.Object({
|
|
4
|
+
accountId: Type.Optional(Type.String()),
|
|
5
|
+
}, { additionalProperties: false });
|
|
6
|
+
export const WhatsAppMessagesParamsSchema = Type.Object({
|
|
7
|
+
jid: NonEmptyString,
|
|
8
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 200 })),
|
|
9
|
+
accountId: Type.Optional(Type.String()),
|
|
10
|
+
}, { additionalProperties: false });
|
|
11
|
+
export const WhatsAppGroupInfoParamsSchema = Type.Object({
|
|
12
|
+
jid: NonEmptyString,
|
|
13
|
+
accountId: Type.Optional(Type.String()),
|
|
14
|
+
}, { additionalProperties: false });
|
|
15
|
+
export const WhatsAppSetActivationParamsSchema = Type.Object({
|
|
16
|
+
jid: NonEmptyString,
|
|
17
|
+
activation: Type.Union([Type.Literal("always"), Type.Literal("mention"), Type.Literal("off")]),
|
|
18
|
+
accountId: Type.Optional(Type.String()),
|
|
19
|
+
}, { additionalProperties: false });
|
|
20
|
+
export const WhatsAppSendMessageParamsSchema = Type.Object({
|
|
21
|
+
jid: NonEmptyString,
|
|
22
|
+
body: NonEmptyString,
|
|
23
|
+
accountId: Type.Optional(Type.String()),
|
|
24
|
+
}, { additionalProperties: false });
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
const SECRET_FILE = path.join(os.homedir(), ".taskmaster", "identity", "public-session.secret");
|
|
6
|
+
let _cachedSecret = null;
|
|
7
|
+
/**
|
|
8
|
+
* Load the HMAC secret from disk, generating and persisting it if it does not exist.
|
|
9
|
+
* The secret is a 32-byte value stored as a 64-char hex string.
|
|
10
|
+
* Cached in memory after first load.
|
|
11
|
+
*/
|
|
12
|
+
export function loadOrCreateSessionSecret() {
|
|
13
|
+
if (_cachedSecret)
|
|
14
|
+
return _cachedSecret;
|
|
15
|
+
try {
|
|
16
|
+
const raw = fs.readFileSync(SECRET_FILE, "utf8").trim();
|
|
17
|
+
if (/^[0-9a-f]{64}$/.test(raw)) {
|
|
18
|
+
_cachedSecret = raw;
|
|
19
|
+
return raw;
|
|
20
|
+
}
|
|
21
|
+
// File exists but content is not a valid 64-char hex secret — regenerate.
|
|
22
|
+
// This invalidates all previously issued session tokens; users will re-authenticate.
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// File absent or unreadable — fall through to generate.
|
|
26
|
+
}
|
|
27
|
+
const secret = randomBytes(32).toString("hex");
|
|
28
|
+
fs.mkdirSync(path.dirname(SECRET_FILE), { recursive: true });
|
|
29
|
+
fs.writeFileSync(SECRET_FILE, secret, { mode: 0o600 });
|
|
30
|
+
_cachedSecret = secret;
|
|
31
|
+
return secret;
|
|
32
|
+
}
|
|
33
|
+
/** Compute HMAC-SHA256(secret, sessionKey) and return as lowercase hex. */
|
|
34
|
+
export function signSessionKey(secret, sessionKey) {
|
|
35
|
+
return createHmac("sha256", secret).update(sessionKey).digest("hex");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Verify that token == HMAC-SHA256(secret, sessionKey).
|
|
39
|
+
* Uses timingSafeEqual to prevent timing attacks.
|
|
40
|
+
* Returns false for tokens of wrong length without performing the comparison.
|
|
41
|
+
*/
|
|
42
|
+
export function verifySessionToken(secret, sessionKey, token) {
|
|
43
|
+
if (!token || token.length !== 64)
|
|
44
|
+
return false;
|
|
45
|
+
const expected = signSessionKey(secret, sessionKey);
|
|
46
|
+
try {
|
|
47
|
+
return timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(token, "hex"));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* Base path: /public/api/v1/:accountId/
|
|
5
5
|
*
|
|
6
6
|
* Endpoints:
|
|
7
|
-
* POST /session — create an anonymous session (returns
|
|
7
|
+
* POST /session — create an anonymous session (returns session_key and session_token)
|
|
8
8
|
* POST /otp/request — request an OTP code (phone via WhatsApp/SMS, or email via Brevo)
|
|
9
|
-
* POST /otp/verify — verify OTP and get a verified session (returns
|
|
9
|
+
* POST /otp/verify — verify OTP and get a verified session (returns session_key and session_token)
|
|
10
10
|
* POST /chat — send a message, receive the agent reply (sync or SSE stream)
|
|
11
11
|
* GET /chat/history — retrieve past messages for a session
|
|
12
12
|
* POST /chat/abort — cancel an in-progress agent run
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
*
|
|
15
15
|
* Authentication mirrors the public chat widget: anonymous sessions use a
|
|
16
16
|
* client-provided identity string; verified sessions use OTP sent via phone
|
|
17
|
-
* (WhatsApp with SMS fallback) or email (Brevo). The
|
|
18
|
-
* from /session or /otp/verify
|
|
19
|
-
* subsequent requests.
|
|
17
|
+
* (WhatsApp with SMS fallback) or email (Brevo). The session_key and
|
|
18
|
+
* session_token returned from /session or /otp/verify are passed via
|
|
19
|
+
* X-Session-Key and X-Session-Token headers on subsequent requests.
|
|
20
20
|
*
|
|
21
21
|
* The chat endpoint uses `dispatchInboundMessage` — the same full pipeline
|
|
22
22
|
* as the WebSocket `chat.send` handler — so filler messages, internal hooks,
|
|
@@ -26,6 +26,7 @@ import { randomUUID } from "node:crypto";
|
|
|
26
26
|
import fs from "node:fs";
|
|
27
27
|
import path from "node:path";
|
|
28
28
|
import { resolveAgentWorkspaceDir, resolveSessionAgentId } from "../agents/agent-scope.js";
|
|
29
|
+
import { loadOrCreateSessionSecret, signSessionKey, verifySessionToken, } from "./public-chat/session-token.js";
|
|
29
30
|
import { resolveEffectiveMessagesConfig, resolveIdentityName } from "../agents/identity.js";
|
|
30
31
|
import { dispatchInboundMessage } from "../auto-reply/dispatch.js";
|
|
31
32
|
import { resolveAgentBoundAccountId } from "../routing/bindings.js";
|
|
@@ -50,7 +51,7 @@ const API_PREFIX = "/public/api/v1/";
|
|
|
50
51
|
function setCorsHeaders(res) {
|
|
51
52
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
52
53
|
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
53
|
-
res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Session-Key");
|
|
54
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, X-Session-Key, X-Session-Token");
|
|
54
55
|
res.setHeader("Access-Control-Max-Age", "86400");
|
|
55
56
|
}
|
|
56
57
|
function sendNotFound(res) {
|
|
@@ -86,6 +87,29 @@ function getSessionKeyHeader(req) {
|
|
|
86
87
|
return raw[0]?.trim();
|
|
87
88
|
return undefined;
|
|
88
89
|
}
|
|
90
|
+
function getSessionTokenHeader(req) {
|
|
91
|
+
const raw = req.headers["x-session-token"];
|
|
92
|
+
if (typeof raw === "string")
|
|
93
|
+
return raw.trim();
|
|
94
|
+
if (Array.isArray(raw))
|
|
95
|
+
return raw[0]?.trim();
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Validate both the session key format and its HMAC token.
|
|
100
|
+
* Returns the session key string on success, null on any failure.
|
|
101
|
+
*/
|
|
102
|
+
function validateSession(req) {
|
|
103
|
+
const sessionKey = validateSessionKey(getSessionKeyHeader(req));
|
|
104
|
+
if (!sessionKey)
|
|
105
|
+
return null;
|
|
106
|
+
const token = getSessionTokenHeader(req);
|
|
107
|
+
if (!token)
|
|
108
|
+
return null;
|
|
109
|
+
if (!verifySessionToken(loadOrCreateSessionSecret(), sessionKey, token))
|
|
110
|
+
return null;
|
|
111
|
+
return sessionKey;
|
|
112
|
+
}
|
|
89
113
|
/** Minimal phone format check: starts with +, digits only, 7–15 digits. */
|
|
90
114
|
function isValidPhone(phone) {
|
|
91
115
|
return /^\+\d{7,15}$/.test(phone);
|
|
@@ -213,7 +237,8 @@ async function handleSession(req, res, accountId, cfg, maxBodyBytes) {
|
|
|
213
237
|
const agentId = resolvePublicAgentId(cfg, accountId);
|
|
214
238
|
const identifier = `anon-${sessionId}`;
|
|
215
239
|
const sessionKey = buildPublicSessionKey(agentId, identifier);
|
|
216
|
-
|
|
240
|
+
const sessionToken = signSessionKey(loadOrCreateSessionSecret(), sessionKey);
|
|
241
|
+
sendJson(res, 200, { session_key: sessionKey, session_token: sessionToken, agent_id: agentId });
|
|
217
242
|
}
|
|
218
243
|
// ---------------------------------------------------------------------------
|
|
219
244
|
// Route: POST /otp/request
|
|
@@ -351,8 +376,10 @@ async function handleOtpVerify(req, res, accountId, cfg, maxBodyBytes) {
|
|
|
351
376
|
}
|
|
352
377
|
const agentId = resolvePublicAgentId(cfg, accountId);
|
|
353
378
|
const sessionKey = buildPublicSessionKey(agentId, identifier);
|
|
379
|
+
const sessionToken = signSessionKey(loadOrCreateSessionSecret(), sessionKey);
|
|
354
380
|
sendJson(res, 200, {
|
|
355
381
|
session_key: sessionKey,
|
|
382
|
+
session_token: sessionToken,
|
|
356
383
|
agent_id: agentId,
|
|
357
384
|
identifier,
|
|
358
385
|
// Backward-compat: include named field matching the identifier type.
|
|
@@ -368,9 +395,9 @@ async function handleChat(req, res, _accountId, cfg, maxBodyBytes) {
|
|
|
368
395
|
sendMethodNotAllowed(res);
|
|
369
396
|
return;
|
|
370
397
|
}
|
|
371
|
-
const sessionKey =
|
|
398
|
+
const sessionKey = validateSession(req);
|
|
372
399
|
if (!sessionKey) {
|
|
373
|
-
sendInvalidRequest(res, "X-Session-Key
|
|
400
|
+
sendInvalidRequest(res, "X-Session-Key and X-Session-Token headers required (obtain from /session or /otp/verify)");
|
|
374
401
|
return;
|
|
375
402
|
}
|
|
376
403
|
const body = await readJsonBodyOrError(req, res, maxBodyBytes);
|
|
@@ -693,9 +720,9 @@ async function handleChatHistory(req, res) {
|
|
|
693
720
|
sendMethodNotAllowed(res, "GET");
|
|
694
721
|
return;
|
|
695
722
|
}
|
|
696
|
-
const sessionKey =
|
|
723
|
+
const sessionKey = validateSession(req);
|
|
697
724
|
if (!sessionKey) {
|
|
698
|
-
sendInvalidRequest(res, "X-Session-Key
|
|
725
|
+
sendInvalidRequest(res, "X-Session-Key and X-Session-Token headers required");
|
|
699
726
|
return;
|
|
700
727
|
}
|
|
701
728
|
const { cfg, storePath, entry } = loadSessionEntry(sessionKey);
|
|
@@ -736,9 +763,9 @@ async function handleChatAbort(req, res, maxBodyBytes) {
|
|
|
736
763
|
sendMethodNotAllowed(res);
|
|
737
764
|
return;
|
|
738
765
|
}
|
|
739
|
-
const sessionKey =
|
|
766
|
+
const sessionKey = validateSession(req);
|
|
740
767
|
if (!sessionKey) {
|
|
741
|
-
sendInvalidRequest(res, "X-Session-Key
|
|
768
|
+
sendInvalidRequest(res, "X-Session-Key and X-Session-Token headers required");
|
|
742
769
|
return;
|
|
743
770
|
}
|
|
744
771
|
const body = await readJsonBodyOrError(req, res, maxBodyBytes);
|
|
@@ -27,6 +27,8 @@ const PROVIDER_CATALOG = [
|
|
|
27
27
|
{ id: "brave", name: "Brave", category: "Web Search" },
|
|
28
28
|
{ id: "elevenlabs", name: "ElevenLabs", category: "Voice" },
|
|
29
29
|
{ id: "brevo", name: "Brevo", category: "Email" },
|
|
30
|
+
{ id: "stripe", name: "Stripe", category: "Payments" },
|
|
31
|
+
{ id: "stripe_webhook_secret", name: "Stripe Webhook Secret", category: "Payments" },
|
|
30
32
|
];
|
|
31
33
|
const VALID_PROVIDER_IDS = new Set(PROVIDER_CATALOG.map((p) => p.id));
|
|
32
34
|
export const apikeysHandlers = {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { getResolvedLoggerSettings } from "../../logging.js";
|
|
4
|
+
import { levelToMinLevel, ALLOWED_LOG_LEVELS } from "../../logging/levels.js";
|
|
5
|
+
import { parseLogLine } from "../../logging/parse-log-line.js";
|
|
4
6
|
import { ErrorCodes, errorShape, formatValidationErrors, validateLogsTailParams, } from "../protocol/index.js";
|
|
5
7
|
const DEFAULT_LIMIT = 500;
|
|
6
8
|
const DEFAULT_MAX_BYTES = 250_000;
|
|
@@ -134,7 +136,21 @@ export const logsHandlers = {
|
|
|
134
136
|
limit: p.limit ?? DEFAULT_LIMIT,
|
|
135
137
|
maxBytes: p.maxBytes ?? DEFAULT_MAX_BYTES,
|
|
136
138
|
});
|
|
137
|
-
|
|
139
|
+
const minLevelNum = p.minLevel !== undefined ? levelToMinLevel(p.minLevel) : undefined;
|
|
140
|
+
const filteredLines = minLevelNum !== undefined
|
|
141
|
+
? result.lines.filter((line) => {
|
|
142
|
+
const parsed = parseLogLine(line);
|
|
143
|
+
if (!parsed || parsed.level === undefined)
|
|
144
|
+
return true; // keep unparseable
|
|
145
|
+
const levelNum = ALLOWED_LOG_LEVELS.includes(parsed.level)
|
|
146
|
+
? levelToMinLevel(parsed.level)
|
|
147
|
+
: undefined;
|
|
148
|
+
if (levelNum === undefined)
|
|
149
|
+
return true; // unknown level — keep
|
|
150
|
+
return levelNum <= minLevelNum;
|
|
151
|
+
})
|
|
152
|
+
: result.lines;
|
|
153
|
+
respond(true, { file, ...result, lines: filteredLines }, undefined);
|
|
138
154
|
}
|
|
139
155
|
catch (err) {
|
|
140
156
|
respond(false, undefined, errorShape(ErrorCodes.UNAVAILABLE, `log read failed: ${String(err)}`));
|
|
@@ -8,6 +8,7 @@ import { ErrorCodes, errorShape } from "../protocol/index.js";
|
|
|
8
8
|
import { requestOtp, verifyOtp } from "../public-chat/otp.js";
|
|
9
9
|
import { deliverOtp, hasPhoneMethod, normalizeVerifyMethods } from "../public-chat/deliver-otp.js";
|
|
10
10
|
import { buildPublicSessionKey, resolvePublicAgentId } from "../public-chat/session.js";
|
|
11
|
+
import { loadOrCreateSessionSecret, signSessionKey } from "../public-chat/session-token.js";
|
|
11
12
|
/** Strip spaces, dashes, and parentheses from a phone number. */
|
|
12
13
|
function normalizePhone(raw) {
|
|
13
14
|
return raw.replace(/[\s\-()]/g, "");
|
|
@@ -155,9 +156,11 @@ export const publicChatHandlers = {
|
|
|
155
156
|
}
|
|
156
157
|
const agentId = resolvePublicAgentId(cfg, accountId);
|
|
157
158
|
const sessionKey = buildPublicSessionKey(agentId, identifier);
|
|
159
|
+
const sessionToken = signSessionKey(loadOrCreateSessionSecret(), sessionKey); // secret is memoised after first load
|
|
158
160
|
respond(true, {
|
|
159
161
|
ok: true,
|
|
160
162
|
sessionKey,
|
|
163
|
+
sessionToken,
|
|
161
164
|
agentId,
|
|
162
165
|
identifier,
|
|
163
166
|
// Backward compat: include named field matching the identifier type.
|
|
@@ -188,9 +191,11 @@ export const publicChatHandlers = {
|
|
|
188
191
|
const agentId = resolvePublicAgentId(cfg, accountId);
|
|
189
192
|
const identifier = `anon-${cookieId}`;
|
|
190
193
|
const sessionKey = buildPublicSessionKey(agentId, identifier);
|
|
194
|
+
const sessionToken = signSessionKey(loadOrCreateSessionSecret(), sessionKey); // secret is memoised after first load
|
|
191
195
|
respond(true, {
|
|
192
196
|
ok: true,
|
|
193
197
|
sessionKey,
|
|
198
|
+
sessionToken,
|
|
194
199
|
agentId,
|
|
195
200
|
});
|
|
196
201
|
},
|
|
@@ -30,6 +30,18 @@ function extractTextFromContentBlocks(blocks) {
|
|
|
30
30
|
}
|
|
31
31
|
return parts.join("\n");
|
|
32
32
|
}
|
|
33
|
+
/** Detect soft-failure tool results: content is JSON with success === false. */
|
|
34
|
+
function isToolResultSoftError(content) {
|
|
35
|
+
if (!content.trim().startsWith("{"))
|
|
36
|
+
return false;
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(content);
|
|
39
|
+
return parsed.success === false;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
33
45
|
/** Format tool input as readable key=value pairs instead of raw JSON. */
|
|
34
46
|
function formatToolInput(input) {
|
|
35
47
|
if (input == null)
|
|
@@ -76,11 +88,12 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
|
|
|
76
88
|
return entries;
|
|
77
89
|
}
|
|
78
90
|
if (line.type === "tool_result") {
|
|
79
|
-
const
|
|
91
|
+
const rawContent = line.result != null
|
|
80
92
|
? typeof line.result === "string"
|
|
81
93
|
? line.result
|
|
82
94
|
: JSON.stringify(line.result)
|
|
83
95
|
: "";
|
|
96
|
+
const content = isToolResultSoftError(rawContent) ? `[error] ${rawContent}` : rawContent;
|
|
84
97
|
entries.push({
|
|
85
98
|
sessionId,
|
|
86
99
|
sessionKey,
|
|
@@ -102,7 +115,10 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
|
|
|
102
115
|
if (msg.role === "toolResult") {
|
|
103
116
|
const textParts = [];
|
|
104
117
|
for (const block of contentBlocks) {
|
|
105
|
-
if (block &&
|
|
118
|
+
if (block &&
|
|
119
|
+
typeof block === "object" &&
|
|
120
|
+
typeof block.text === "string" &&
|
|
121
|
+
block.text.trim()) {
|
|
106
122
|
textParts.push(block.text.trim());
|
|
107
123
|
}
|
|
108
124
|
}
|
|
@@ -112,13 +128,14 @@ function expandLineToEntries(line, sessionId, sessionKey, agentId, fileMtimeMs)
|
|
|
112
128
|
const content = textParts.length > 0 ? textParts.join("\n") : "(empty result)";
|
|
113
129
|
const toolName = typeof msg.toolName === "string" ? msg.toolName : undefined;
|
|
114
130
|
const toolCallId = typeof msg.toolCallId === "string" ? msg.toolCallId : undefined;
|
|
131
|
+
const isError = msg.isError || isToolResultSoftError(content);
|
|
115
132
|
entries.push({
|
|
116
133
|
sessionId,
|
|
117
134
|
sessionKey,
|
|
118
135
|
agentId,
|
|
119
136
|
timestamp: ts,
|
|
120
137
|
type: "tool_result",
|
|
121
|
-
content:
|
|
138
|
+
content: isError ? `[error] ${content}` : content,
|
|
122
139
|
...(toolName ? { toolName } : {}),
|
|
123
140
|
...(toolCallId ? { toolCallId } : {}),
|
|
124
141
|
...(model ? { model } : {}),
|
|
@@ -259,6 +276,7 @@ export const sessionsTranscriptHandlers = {
|
|
|
259
276
|
}
|
|
260
277
|
const p = params;
|
|
261
278
|
const limit = p.limit ?? DEFAULT_LIMIT;
|
|
279
|
+
const errorsOnly = p.errorsOnly === true;
|
|
262
280
|
const inputCursors = p.cursors ?? {};
|
|
263
281
|
const agentFilter = p.agents && p.agents.length > 0 ? new Set(p.agents) : null;
|
|
264
282
|
try {
|
|
@@ -343,13 +361,19 @@ export const sessionsTranscriptHandlers = {
|
|
|
343
361
|
}
|
|
344
362
|
}
|
|
345
363
|
}
|
|
346
|
-
//
|
|
347
|
-
|
|
364
|
+
// Filter to errors only if requested: session-level errors OR tool results that failed
|
|
365
|
+
const filtered = errorsOnly
|
|
366
|
+
? allEntries.filter((e) => e.type === "error" ||
|
|
367
|
+
(e.type === "tool_result" &&
|
|
368
|
+
typeof e.content === "string" &&
|
|
369
|
+
e.content.startsWith("[error]")))
|
|
370
|
+
: allEntries;
|
|
371
|
+
filtered.sort((a, b) => {
|
|
348
372
|
const ta = new Date(a.timestamp).getTime();
|
|
349
373
|
const tb = new Date(b.timestamp).getTime();
|
|
350
374
|
return tb - ta;
|
|
351
375
|
});
|
|
352
|
-
const entries =
|
|
376
|
+
const entries = filtered.slice(0, limit);
|
|
353
377
|
// Include configured agents so UI filter chips appear even when an
|
|
354
378
|
// agent has no sessions yet. When an agent filter is active (account
|
|
355
379
|
// scoping), only add config agents that match the filter — otherwise
|