@hailer/mcp 1.0.29 → 1.1.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/.claude/.session-checked +1 -0
- package/.claude/agents/agent-ada-skill-builder.md +10 -2
- package/.claude/agents/agent-alejandro-function-fields.md +104 -37
- package/.claude/agents/agent-bjorn-config-audit.md +41 -21
- package/.claude/agents/agent-builder-agent-creator.md +13 -3
- package/.claude/agents/agent-code-simplifier.md +53 -0
- package/.claude/agents/agent-dmitri-activity-crud.md +126 -11
- package/.claude/agents/agent-giuseppe-app-builder.md +212 -22
- package/.claude/agents/agent-gunther-mcp-tools.md +7 -36
- package/.claude/agents/agent-helga-workflow-config.md +75 -10
- package/.claude/agents/agent-igor-activity-mover-automation.md +125 -0
- package/.claude/agents/agent-ingrid-doc-templates.md +164 -36
- package/.claude/agents/agent-ivan-monolith.md +154 -0
- package/.claude/agents/agent-kenji-data-reader.md +15 -8
- package/.claude/agents/agent-lars-code-inspector.md +56 -8
- package/.claude/agents/agent-marco-mockup-builder.md +110 -0
- package/.claude/agents/agent-marcus-api-documenter.md +323 -0
- package/.claude/agents/agent-marketplace-publisher.md +232 -72
- package/.claude/agents/agent-marketplace-reviewer.md +255 -79
- package/.claude/agents/agent-permissions-handler.md +208 -0
- package/.claude/agents/agent-simple-writer.md +48 -0
- package/.claude/agents/agent-svetlana-code-review.md +127 -14
- package/.claude/agents/agent-tanya-test-runner.md +333 -0
- package/.claude/agents/agent-ui-designer.md +100 -0
- package/.claude/agents/agent-viktor-sql-insights.md +19 -6
- package/.claude/agents/agent-web-search.md +55 -0
- package/.claude/agents/agent-yevgeni-discussions.md +7 -1
- package/.claude/agents/agent-zara-zapier.md +159 -0
- package/.claude/commands/app-squad.md +135 -0
- package/.claude/commands/audit-squad.md +158 -0
- package/.claude/commands/autoplan.md +563 -0
- package/.claude/commands/cleanup-squad.md +98 -0
- package/.claude/commands/config-squad.md +106 -0
- package/.claude/commands/crud-squad.md +87 -0
- package/.claude/commands/data-squad.md +97 -0
- package/.claude/commands/debug-squad.md +303 -0
- package/.claude/commands/doc-squad.md +65 -0
- package/.claude/commands/handoff.md +137 -0
- package/.claude/commands/health.md +49 -0
- package/.claude/commands/help.md +2 -1
- package/.claude/commands/help:agents.md +96 -16
- package/.claude/commands/help:commands.md +55 -11
- package/.claude/commands/help:faq.md +16 -1
- package/.claude/commands/help:skills.md +93 -0
- package/.claude/commands/hotfix-squad.md +112 -0
- package/.claude/commands/integration-squad.md +82 -0
- package/.claude/commands/janitor-squad.md +167 -0
- package/.claude/commands/learn-auto.md +120 -0
- package/.claude/commands/learn.md +120 -0
- package/.claude/commands/mcp-list.md +27 -0
- package/.claude/commands/onboard-squad.md +140 -0
- package/.claude/commands/plan-workspace.md +732 -0
- package/.claude/commands/prd.md +131 -0
- package/.claude/commands/project-status.md +82 -0
- package/.claude/commands/publish.md +138 -0
- package/.claude/commands/recap.md +69 -0
- package/.claude/commands/restore.md +64 -0
- package/.claude/commands/review-squad.md +152 -0
- package/.claude/commands/save.md +24 -0
- package/.claude/commands/stats.md +19 -0
- package/.claude/commands/swarm.md +210 -0
- package/.claude/commands/tool-builder.md +3 -1
- package/.claude/commands/ws-pull.md +1 -1
- package/.claude/commands/yolo-off.md +17 -0
- package/.claude/commands/yolo.md +82 -0
- package/.claude/hooks/_shared-memory.cjs +305 -0
- package/.claude/hooks/_utils.cjs +134 -0
- package/.claude/hooks/agent-failure-detector.cjs +164 -79
- package/.claude/hooks/agent-usage-logger.cjs +204 -0
- package/.claude/hooks/app-edit-guard.cjs +20 -4
- package/.claude/hooks/auto-learn.cjs +316 -0
- package/.claude/hooks/bash-guard.cjs +282 -0
- package/.claude/hooks/builder-mode-manager.cjs +183 -54
- package/.claude/hooks/bulk-activity-guard.cjs +283 -0
- package/.claude/hooks/context-watchdog.cjs +292 -0
- package/.claude/hooks/delegation-reminder.cjs +478 -0
- package/.claude/hooks/design-system-lint.cjs +283 -0
- package/.claude/hooks/post-scaffold-hook.cjs +16 -3
- package/.claude/hooks/prompt-guard.cjs +366 -0
- package/.claude/hooks/publish-template-guard.cjs +16 -0
- package/.claude/hooks/session-start.cjs +35 -0
- package/.claude/hooks/shared-memory-writer.cjs +147 -0
- package/.claude/hooks/skill-injector.cjs +140 -0
- package/.claude/hooks/skill-usage-logger.cjs +258 -0
- package/.claude/hooks/src-edit-guard.cjs +16 -1
- package/.claude/hooks/sync-marketplace-agents.cjs +53 -8
- package/.claude/scripts/yolo-toggle.cjs +142 -0
- package/.claude/settings.json +141 -14
- package/.claude/skills/SDK-activity-patterns/SKILL.md +428 -0
- package/.claude/skills/SDK-document-templates/SKILL.md +1033 -0
- package/.claude/skills/SDK-function-fields/SKILL.md +542 -0
- package/.claude/skills/SDK-generate-skill/SKILL.md +92 -0
- package/.claude/skills/SDK-init-skill/SKILL.md +127 -0
- package/.claude/skills/SDK-insight-queries/SKILL.md +787 -0
- package/.claude/skills/SDK-ws-config-skill/SKILL.md +1139 -0
- package/.claude/skills/agent-structure/SKILL.md +98 -0
- package/.claude/skills/api-documentation-patterns/SKILL.md +474 -0
- package/.claude/skills/chrome-mcp-reference/SKILL.md +370 -0
- package/.claude/skills/delegation-routing/SKILL.md +202 -0
- package/.claude/skills/frontend-design/SKILL.md +254 -0
- package/.claude/skills/hailer-activity-mover/SKILL.md +213 -0
- package/.claude/skills/hailer-api-client/SKILL.md +518 -0
- package/.claude/skills/hailer-app-builder/SKILL.md +939 -11
- package/.claude/skills/hailer-apps-pictures/SKILL.md +269 -0
- package/.claude/skills/hailer-design-system/SKILL.md +235 -0
- package/.claude/skills/hailer-monolith-automations/SKILL.md +686 -0
- package/.claude/skills/hailer-permissions-system/SKILL.md +121 -0
- package/.claude/skills/hailer-project-protocol/SKILL.md +488 -0
- package/.claude/skills/hailer-rest-api/SKILL.md +61 -0
- package/.claude/skills/hailer-rest-api/hailer-activities.md +184 -0
- package/.claude/skills/hailer-rest-api/hailer-admin.md +473 -0
- package/.claude/skills/hailer-rest-api/hailer-calendar.md +256 -0
- package/.claude/skills/hailer-rest-api/hailer-feed.md +249 -0
- package/.claude/skills/hailer-rest-api/hailer-insights.md +195 -0
- package/.claude/skills/hailer-rest-api/hailer-messaging.md +276 -0
- package/.claude/skills/hailer-rest-api/hailer-workflows.md +283 -0
- package/.claude/skills/insight-join-patterns/SKILL.md +3 -0
- package/.claude/skills/integration-patterns/SKILL.md +421 -0
- package/.claude/skills/json-only-output/SKILL.md +52 -12
- package/.claude/skills/lsp-setup/SKILL.md +160 -0
- package/.claude/skills/mcp-direct-tools/SKILL.md +153 -0
- package/.claude/skills/optional-parameters/SKILL.md +32 -23
- package/.claude/skills/publish-hailer-app/SKILL.md +76 -12
- package/.claude/skills/testing-patterns/SKILL.md +630 -0
- package/.claude/skills/tool-builder/SKILL.md +250 -0
- package/.claude/skills/tool-parameter-usage/SKILL.md +59 -45
- package/.claude/skills/tool-response-verification/SKILL.md +82 -48
- package/.claude/skills/zapier-hailer-patterns/SKILL.md +581 -0
- package/.env.example +26 -7
- package/CLAUDE.md +290 -224
- package/dist/CLAUDE.md +370 -0
- package/dist/app.d.ts +1 -1
- package/dist/app.js +101 -101
- package/dist/bot/bot-config.d.ts +26 -0
- package/dist/bot/bot-config.js +135 -0
- package/dist/bot/bot-manager.d.ts +40 -0
- package/dist/bot/bot-manager.js +137 -0
- package/dist/bot/bot.d.ts +127 -0
- package/dist/bot/bot.js +1328 -0
- package/dist/bot/operation-logger.d.ts +28 -0
- package/dist/bot/operation-logger.js +132 -0
- package/dist/bot/services/conversation-manager.d.ts +60 -0
- package/dist/bot/services/conversation-manager.js +246 -0
- package/dist/bot/services/index.d.ts +9 -0
- package/dist/bot/services/index.js +18 -0
- package/dist/bot/services/message-classifier.d.ts +42 -0
- package/dist/bot/services/message-classifier.js +228 -0
- package/dist/bot/services/message-formatter.d.ts +88 -0
- package/dist/bot/services/message-formatter.js +411 -0
- package/dist/bot/services/session-logger.d.ts +162 -0
- package/dist/bot/services/session-logger.js +724 -0
- package/dist/bot/services/token-billing.d.ts +78 -0
- package/dist/bot/services/token-billing.js +233 -0
- package/dist/bot/services/types.d.ts +169 -0
- package/dist/bot/services/types.js +12 -0
- package/dist/bot/services/typing-indicator.d.ts +23 -0
- package/dist/bot/services/typing-indicator.js +60 -0
- package/dist/bot/services/workspace-schema-cache.d.ts +122 -0
- package/dist/bot/services/workspace-schema-cache.js +506 -0
- package/dist/bot/tool-executor.d.ts +28 -0
- package/dist/bot/tool-executor.js +48 -0
- package/dist/bot/workspace-overview.d.ts +12 -0
- package/dist/bot/workspace-overview.js +94 -0
- package/dist/cli.d.ts +1 -8
- package/dist/cli.js +1 -253
- package/dist/config.d.ts +96 -3
- package/dist/config.js +148 -37
- package/dist/core.d.ts +5 -0
- package/dist/core.js +61 -8
- package/dist/lib/discussion-lock.d.ts +42 -0
- package/dist/lib/discussion-lock.js +110 -0
- package/dist/lib/logger.d.ts +0 -1
- package/dist/lib/logger.js +39 -23
- package/dist/lib/request-logger.d.ts +77 -0
- package/dist/lib/request-logger.js +147 -0
- package/dist/mcp/UserContextCache.js +16 -13
- package/dist/mcp/hailer-clients.js +18 -17
- package/dist/mcp/signal-handler.js +29 -13
- package/dist/mcp/tool-registry.d.ts +4 -15
- package/dist/mcp/tool-registry.js +94 -32
- package/dist/mcp/tools/activity.js +28 -69
- package/dist/mcp/tools/app-core.js +9 -4
- package/dist/mcp/tools/app-marketplace.js +22 -12
- package/dist/mcp/tools/app-member.js +5 -2
- package/dist/mcp/tools/app-scaffold.js +32 -18
- package/dist/mcp/tools/bot-config/constants.d.ts +23 -0
- package/dist/mcp/tools/bot-config/constants.js +94 -0
- package/dist/mcp/tools/bot-config/core.d.ts +253 -0
- package/dist/mcp/tools/bot-config/core.js +2456 -0
- package/dist/mcp/tools/bot-config/index.d.ts +10 -0
- package/dist/mcp/tools/bot-config/index.js +59 -0
- package/dist/mcp/tools/bot-config/tools.d.ts +7 -0
- package/dist/mcp/tools/bot-config/tools.js +15 -0
- package/dist/mcp/tools/bot-config/types.d.ts +50 -0
- package/dist/mcp/tools/bot-config/types.js +6 -0
- package/dist/mcp/tools/discussion.js +107 -77
- package/dist/mcp/tools/document.d.ts +11 -0
- package/dist/mcp/tools/document.js +741 -0
- package/dist/mcp/tools/file.js +5 -2
- package/dist/mcp/tools/insight.js +36 -12
- package/dist/mcp/tools/investigate.d.ts +9 -0
- package/dist/mcp/tools/investigate.js +254 -0
- package/dist/mcp/tools/user.d.ts +2 -4
- package/dist/mcp/tools/user.js +9 -50
- package/dist/mcp/tools/workflow.d.ts +1 -0
- package/dist/mcp/tools/workflow.js +164 -52
- package/dist/mcp/utils/hailer-api-client.js +26 -17
- package/dist/mcp/webhook-handler.d.ts +64 -3
- package/dist/mcp/webhook-handler.js +219 -9
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.js +237 -25
- package/dist/plugins/bug-fixer/index.d.ts +2 -0
- package/dist/plugins/bug-fixer/index.js +18 -0
- package/dist/plugins/bug-fixer/tools.d.ts +45 -0
- package/dist/plugins/bug-fixer/tools.js +1096 -0
- package/package.json +10 -10
- package/scripts/test-hal-tools.ts +154 -0
- package/.claude/agents/agent-nora-name-functions.md +0 -123
- package/.claude/assistant-knowledge.md +0 -23
- package/.claude/commands/install-plugin.md +0 -261
- package/.claude/commands/list-plugins.md +0 -42
- package/.claude/commands/marketplace-setup.md +0 -33
- package/.claude/commands/publish-plugin.md +0 -55
- package/.claude/commands/uninstall-plugin.md +0 -87
- package/.claude/hooks/interactive-mode.cjs +0 -87
- package/.claude/hooks/mcp-server-guard.cjs +0 -108
- package/.claude/skills/marketplace-publishing.md +0 -155
- package/dist/bot/chat-bot.d.ts +0 -31
- package/dist/bot/chat-bot.js +0 -357
- package/dist/mcp/tools/metrics.d.ts +0 -13
- package/dist/mcp/tools/metrics.js +0 -546
- package/dist/stdio-server.d.ts +0 -14
- package/dist/stdio-server.js +0 -114
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OperationLogger - Centralized inline logging for bot operations.
|
|
3
|
+
*
|
|
4
|
+
* Produces structured single-line log entries for message flow,
|
|
5
|
+
* balance checks, routing, LLM calls, tool calls, and responses.
|
|
6
|
+
*/
|
|
7
|
+
export declare class OperationLogger {
|
|
8
|
+
messageIn(discussionId: string, from: string, content: string): void;
|
|
9
|
+
balanceCheck(discussionId: string, workspaceId: string, balance: number, status: string): void;
|
|
10
|
+
route(discussionId: string, classification: string, model: string, content: string): void;
|
|
11
|
+
llmCall(discussionId: string, model: string, inTokens: number, outTokens: number, cacheHitPct: number, durationSec: number): void;
|
|
12
|
+
toolCall(discussionId: string, toolName: string, summary: string, durationSec: number, status: 'OK' | 'FAIL', errorPreview?: string): void;
|
|
13
|
+
permDenied(discussionId: string, toolName: string, workflowId: string, userId: string): void;
|
|
14
|
+
permFiltered(discussionId: string, toolName: string, before: number, after: number): void;
|
|
15
|
+
messageOut(discussionId: string, charCount: number, tagCount: number): void;
|
|
16
|
+
engage(discussionId: string, trigger: string): void;
|
|
17
|
+
disengage(discussionId: string, nonResponses: number): void;
|
|
18
|
+
coalesce(discussionId: string, count: number): void;
|
|
19
|
+
interrupt(discussionId: string, reason: string): void;
|
|
20
|
+
contextInject(discussionId: string, count: number): void;
|
|
21
|
+
progress(discussionId: string): void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generate a human-readable summary from a tool result.
|
|
25
|
+
* Used by bot.ts executeTools() to produce concise TOOL CALL log summaries.
|
|
26
|
+
*/
|
|
27
|
+
export declare function summarizeToolResult(toolName: string, resultText: string): string;
|
|
28
|
+
//# sourceMappingURL=operation-logger.d.ts.map
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OperationLogger - Centralized inline logging for bot operations.
|
|
4
|
+
*
|
|
5
|
+
* Produces structured single-line log entries for message flow,
|
|
6
|
+
* balance checks, routing, LLM calls, tool calls, and responses.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.OperationLogger = void 0;
|
|
10
|
+
exports.summarizeToolResult = summarizeToolResult;
|
|
11
|
+
const logger_1 = require("../lib/logger");
|
|
12
|
+
const logger = (0, logger_1.createLogger)({ component: 'ops' });
|
|
13
|
+
/** Shorten discussion ID to first 8 chars */
|
|
14
|
+
function did(discussionId) {
|
|
15
|
+
return `d:${discussionId.slice(0, 8)}..`;
|
|
16
|
+
}
|
|
17
|
+
/** Shorten model name for log readability */
|
|
18
|
+
function shortModel(model) {
|
|
19
|
+
if (model.includes('sonnet-4-5'))
|
|
20
|
+
return 'sonnet-4.5';
|
|
21
|
+
if (model.includes('haiku-4-5'))
|
|
22
|
+
return 'haiku-4.5';
|
|
23
|
+
if (model.includes('sonnet'))
|
|
24
|
+
return 'sonnet';
|
|
25
|
+
if (model.includes('haiku'))
|
|
26
|
+
return 'haiku';
|
|
27
|
+
return model;
|
|
28
|
+
}
|
|
29
|
+
/** Truncate string to maxLen chars, collapsing newlines */
|
|
30
|
+
function trunc(s, maxLen = 80) {
|
|
31
|
+
const clean = s.replace(/\n/g, ' ').trim();
|
|
32
|
+
return clean.length > maxLen ? clean.slice(0, maxLen) + '...' : clean;
|
|
33
|
+
}
|
|
34
|
+
class OperationLogger {
|
|
35
|
+
messageIn(discussionId, from, content) {
|
|
36
|
+
logger.info(`MSG IN | ${did(discussionId)} | from:${from} | "${trunc(content, 60)}"`);
|
|
37
|
+
}
|
|
38
|
+
balanceCheck(discussionId, workspaceId, balance, status) {
|
|
39
|
+
logger.info(`BALANCE | ${did(discussionId)} | ws:${workspaceId.slice(0, 8)}.. | ${balance} tokens | ${status}`);
|
|
40
|
+
}
|
|
41
|
+
route(discussionId, classification, model, content) {
|
|
42
|
+
logger.info(`ROUTE | ${did(discussionId)} | ${classification} -> ${shortModel(model)} | "${trunc(content, 60)}"`);
|
|
43
|
+
}
|
|
44
|
+
llmCall(discussionId, model, inTokens, outTokens, cacheHitPct, durationSec) {
|
|
45
|
+
logger.info(`LLM CALL | ${did(discussionId)} | ${shortModel(model)} | ${inTokens} in / ${outTokens} out | cache: ${cacheHitPct}% | ${durationSec.toFixed(1)}s`);
|
|
46
|
+
}
|
|
47
|
+
toolCall(discussionId, toolName, summary, durationSec, status, errorPreview) {
|
|
48
|
+
let line = `TOOL CALL | ${did(discussionId)} | ${toolName} | ${summary} | ${durationSec.toFixed(1)}s | ${status}`;
|
|
49
|
+
if (errorPreview)
|
|
50
|
+
line += ` | "${trunc(errorPreview)}"`;
|
|
51
|
+
logger.info(line);
|
|
52
|
+
}
|
|
53
|
+
permDenied(discussionId, toolName, workflowId, userId) {
|
|
54
|
+
logger.warn(`PERM DENY | ${did(discussionId)} | ${toolName} | wf:${workflowId.slice(0, 8)}.. | user:${userId.slice(0, 8)}..`);
|
|
55
|
+
}
|
|
56
|
+
permFiltered(discussionId, toolName, before, after) {
|
|
57
|
+
logger.info(`PERM FILT | ${did(discussionId)} | ${toolName} | ${before} -> ${after} workflows`);
|
|
58
|
+
}
|
|
59
|
+
messageOut(discussionId, charCount, tagCount) {
|
|
60
|
+
logger.info(`MSG OUT | ${did(discussionId)} | ${charCount} chars | ${tagCount} tags`);
|
|
61
|
+
}
|
|
62
|
+
engage(discussionId, trigger) {
|
|
63
|
+
logger.info(`ENGAGE | ${did(discussionId)} | ${trigger}`);
|
|
64
|
+
}
|
|
65
|
+
disengage(discussionId, nonResponses) {
|
|
66
|
+
logger.info(`DISENGAGE | ${did(discussionId)} | ${nonResponses} non-responses`);
|
|
67
|
+
}
|
|
68
|
+
coalesce(discussionId, count) {
|
|
69
|
+
logger.info(`COALESCE | ${did(discussionId)} | ${count} messages`);
|
|
70
|
+
}
|
|
71
|
+
interrupt(discussionId, reason) {
|
|
72
|
+
logger.info(`INTERRUPT | ${did(discussionId)} | ${reason}`);
|
|
73
|
+
}
|
|
74
|
+
contextInject(discussionId, count) {
|
|
75
|
+
logger.info(`CTX INJ | ${did(discussionId)} | ${count} messages`);
|
|
76
|
+
}
|
|
77
|
+
progress(discussionId) {
|
|
78
|
+
logger.info(`PROGRESS | ${did(discussionId)}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
exports.OperationLogger = OperationLogger;
|
|
82
|
+
/**
|
|
83
|
+
* Generate a human-readable summary from a tool result.
|
|
84
|
+
* Used by bot.ts executeTools() to produce concise TOOL CALL log summaries.
|
|
85
|
+
*/
|
|
86
|
+
function summarizeToolResult(toolName, resultText) {
|
|
87
|
+
try {
|
|
88
|
+
const json = JSON.parse(resultText);
|
|
89
|
+
// create_activity
|
|
90
|
+
if (toolName === 'create_activity') {
|
|
91
|
+
if (json.created_ids)
|
|
92
|
+
return `${json.created_ids.length} activit${json.created_ids.length === 1 ? 'y' : 'ies'}`;
|
|
93
|
+
if (json.name)
|
|
94
|
+
return `1 activity: ${trunc(json.name, 40)}`;
|
|
95
|
+
}
|
|
96
|
+
// list_activities
|
|
97
|
+
if (toolName === 'list_activities') {
|
|
98
|
+
const count = json.stats?.total ?? json.activities?.length ?? json.length;
|
|
99
|
+
if (count !== undefined)
|
|
100
|
+
return `${count} results`;
|
|
101
|
+
}
|
|
102
|
+
// list_workflows / list_workflows_minimal
|
|
103
|
+
if (toolName.includes('list_workflows')) {
|
|
104
|
+
const count = json.workflows?.length ?? json.length;
|
|
105
|
+
if (count !== undefined)
|
|
106
|
+
return `${count} workflows`;
|
|
107
|
+
}
|
|
108
|
+
// get_workflow_schema
|
|
109
|
+
if (toolName === 'get_workflow_schema') {
|
|
110
|
+
const count = json.fields?.length ?? Object.keys(json.fields || {}).length;
|
|
111
|
+
if (count)
|
|
112
|
+
return `${count} fields`;
|
|
113
|
+
}
|
|
114
|
+
// count_activities
|
|
115
|
+
if (toolName === 'count_activities') {
|
|
116
|
+
if (json.total !== undefined)
|
|
117
|
+
return `${json.total} activities`;
|
|
118
|
+
}
|
|
119
|
+
// search_workspace_users
|
|
120
|
+
if (toolName === 'search_workspace_users') {
|
|
121
|
+
const count = json.users?.length ?? json.length;
|
|
122
|
+
if (count !== undefined)
|
|
123
|
+
return `${count} users`;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Not JSON, fall through
|
|
128
|
+
}
|
|
129
|
+
// Fallback: first 80 chars of result text
|
|
130
|
+
return trunc(resultText);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=operation-logger.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation Manager Service
|
|
3
|
+
*
|
|
4
|
+
* Manages per-discussion conversation state:
|
|
5
|
+
* - LRU cache of conversation histories
|
|
6
|
+
* - Context size management with summarization
|
|
7
|
+
* - Conversation state access
|
|
8
|
+
*/
|
|
9
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
10
|
+
import { Logger } from "../../lib/logger";
|
|
11
|
+
/** Conversation message using Anthropic's MessageParam type */
|
|
12
|
+
export type ConversationMessage = Anthropic.MessageParam;
|
|
13
|
+
export declare class ConversationManager {
|
|
14
|
+
private maxConversations;
|
|
15
|
+
private maxContextMessages;
|
|
16
|
+
private logger;
|
|
17
|
+
private conversationsByDiscussion;
|
|
18
|
+
constructor(maxConversations: number, maxContextMessages: number, logger: Logger);
|
|
19
|
+
/**
|
|
20
|
+
* Get or create conversation history for a specific discussion
|
|
21
|
+
* Each discussion has its own isolated context to prevent bloat
|
|
22
|
+
* Uses LRU eviction when map exceeds maxConversations
|
|
23
|
+
*/
|
|
24
|
+
getConversation(discussionId: string): ConversationMessage[];
|
|
25
|
+
/**
|
|
26
|
+
* Add a message to a conversation
|
|
27
|
+
*/
|
|
28
|
+
addMessage(discussionId: string, message: ConversationMessage): void;
|
|
29
|
+
/**
|
|
30
|
+
* Manage conversation context size - truncate if too large
|
|
31
|
+
* Generates a programmatic summary of dropped messages before truncating
|
|
32
|
+
*/
|
|
33
|
+
manageContextSize(discussionId: string): void;
|
|
34
|
+
/**
|
|
35
|
+
* Build a compact programmatic summary of dropped messages (no LLM call)
|
|
36
|
+
*/
|
|
37
|
+
private summarizeDroppedMessages;
|
|
38
|
+
/**
|
|
39
|
+
* Get conversation state for debugging
|
|
40
|
+
*/
|
|
41
|
+
getState(currentDiscussionId?: string): {
|
|
42
|
+
discussionCount: number;
|
|
43
|
+
currentMessageCount: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Get full conversation for a discussion (for debugging)
|
|
47
|
+
*/
|
|
48
|
+
getFullConversation(discussionId?: string): ConversationMessage[];
|
|
49
|
+
/**
|
|
50
|
+
* Clear all conversations
|
|
51
|
+
*/
|
|
52
|
+
clear(): void;
|
|
53
|
+
/**
|
|
54
|
+
* Prepare conversation for API call with Anthropic prompt caching
|
|
55
|
+
* Adds cache_control to the last assistant message to cache conversation history
|
|
56
|
+
* Returns a COPY - does not modify the original conversation
|
|
57
|
+
*/
|
|
58
|
+
prepareForCaching(conversation: ConversationMessage[]): ConversationMessage[];
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=conversation-manager.d.ts.map
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Conversation Manager Service
|
|
4
|
+
*
|
|
5
|
+
* Manages per-discussion conversation state:
|
|
6
|
+
* - LRU cache of conversation histories
|
|
7
|
+
* - Context size management with summarization
|
|
8
|
+
* - Conversation state access
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.ConversationManager = void 0;
|
|
12
|
+
class ConversationManager {
|
|
13
|
+
maxConversations;
|
|
14
|
+
maxContextMessages;
|
|
15
|
+
logger;
|
|
16
|
+
conversationsByDiscussion = new Map();
|
|
17
|
+
constructor(maxConversations, maxContextMessages, logger) {
|
|
18
|
+
this.maxConversations = maxConversations;
|
|
19
|
+
this.maxContextMessages = maxContextMessages;
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Get or create conversation history for a specific discussion
|
|
24
|
+
* Each discussion has its own isolated context to prevent bloat
|
|
25
|
+
* Uses LRU eviction when map exceeds maxConversations
|
|
26
|
+
*/
|
|
27
|
+
getConversation(discussionId) {
|
|
28
|
+
let conversation = this.conversationsByDiscussion.get(discussionId);
|
|
29
|
+
if (conversation) {
|
|
30
|
+
// Move to end for LRU (delete and re-add to update insertion order)
|
|
31
|
+
this.conversationsByDiscussion.delete(discussionId);
|
|
32
|
+
this.conversationsByDiscussion.set(discussionId, conversation);
|
|
33
|
+
return conversation;
|
|
34
|
+
}
|
|
35
|
+
// Evict oldest conversations if at capacity
|
|
36
|
+
if (this.conversationsByDiscussion.size >= this.maxConversations) {
|
|
37
|
+
const toEvict = this.conversationsByDiscussion.size - this.maxConversations + 1;
|
|
38
|
+
const keys = [...this.conversationsByDiscussion.keys()].slice(0, toEvict);
|
|
39
|
+
for (const key of keys) {
|
|
40
|
+
this.conversationsByDiscussion.delete(key);
|
|
41
|
+
}
|
|
42
|
+
this.logger.debug("Evicted old conversations", { evicted: toEvict, remaining: this.conversationsByDiscussion.size });
|
|
43
|
+
}
|
|
44
|
+
conversation = [];
|
|
45
|
+
this.conversationsByDiscussion.set(discussionId, conversation);
|
|
46
|
+
this.logger.debug("Created new conversation context", { discussionId });
|
|
47
|
+
return conversation;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Add a message to a conversation
|
|
51
|
+
*/
|
|
52
|
+
addMessage(discussionId, message) {
|
|
53
|
+
const conversation = this.getConversation(discussionId);
|
|
54
|
+
conversation.push(message);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Manage conversation context size - truncate if too large
|
|
58
|
+
* Generates a programmatic summary of dropped messages before truncating
|
|
59
|
+
*/
|
|
60
|
+
manageContextSize(discussionId) {
|
|
61
|
+
const conversation = this.getConversation(discussionId);
|
|
62
|
+
if (conversation.length > this.maxContextMessages) {
|
|
63
|
+
const originalCount = conversation.length;
|
|
64
|
+
const keepCount = Math.floor(this.maxContextMessages * 0.6);
|
|
65
|
+
const dropped = conversation.slice(0, -keepCount);
|
|
66
|
+
const toKeep = conversation.slice(-keepCount);
|
|
67
|
+
// Build summary of what's being dropped
|
|
68
|
+
const summary = this.summarizeDroppedMessages(dropped);
|
|
69
|
+
// Merge or prepend summary to preserve user/assistant alternation
|
|
70
|
+
let result;
|
|
71
|
+
if (toKeep.length > 0 && toKeep[0].role === "user") {
|
|
72
|
+
// First kept message is user — merge summary into it to avoid consecutive user messages
|
|
73
|
+
const first = toKeep[0];
|
|
74
|
+
if (typeof first.content === "string") {
|
|
75
|
+
toKeep[0] = { role: "user", content: `${summary}\n\n${first.content}` };
|
|
76
|
+
}
|
|
77
|
+
else if (Array.isArray(first.content)) {
|
|
78
|
+
toKeep[0] = {
|
|
79
|
+
role: "user",
|
|
80
|
+
content: [{ type: "text", text: summary }, ...first.content],
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
result = toKeep;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// First kept message is assistant — safe to prepend user summary
|
|
87
|
+
result = [{ role: "user", content: summary }, ...toKeep];
|
|
88
|
+
}
|
|
89
|
+
this.conversationsByDiscussion.set(discussionId, result);
|
|
90
|
+
this.logger.debug("Context truncated with summary", {
|
|
91
|
+
discussionId,
|
|
92
|
+
originalCount,
|
|
93
|
+
newCount: result.length,
|
|
94
|
+
dropped: dropped.length,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Build a compact programmatic summary of dropped messages (no LLM call)
|
|
100
|
+
*/
|
|
101
|
+
summarizeDroppedMessages(dropped) {
|
|
102
|
+
const senders = new Set();
|
|
103
|
+
const tools = new Set();
|
|
104
|
+
const userSnippets = [];
|
|
105
|
+
for (const msg of dropped) {
|
|
106
|
+
if (msg.role === "user") {
|
|
107
|
+
const content = typeof msg.content === "string"
|
|
108
|
+
? msg.content
|
|
109
|
+
: Array.isArray(msg.content)
|
|
110
|
+
? msg.content
|
|
111
|
+
.filter((b) => b.type === "text")
|
|
112
|
+
.map((b) => b.text)
|
|
113
|
+
.join(" ")
|
|
114
|
+
: "";
|
|
115
|
+
// Extract sender from <incoming from="Name">
|
|
116
|
+
const fromMatch = content.match(/from="([^"]+)"/);
|
|
117
|
+
if (fromMatch)
|
|
118
|
+
senders.add(fromMatch[1]);
|
|
119
|
+
if (content && !content.startsWith("<context")) {
|
|
120
|
+
userSnippets.push(content.slice(0, 100));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
124
|
+
for (const block of msg.content) {
|
|
125
|
+
if (block.type === "tool_use") {
|
|
126
|
+
tools.add(block.name);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const parts = [
|
|
132
|
+
`Prior conversation (${dropped.length} messages truncated):`,
|
|
133
|
+
];
|
|
134
|
+
if (senders.size > 0)
|
|
135
|
+
parts.push(`- Participants: ${[...senders].join(", ")}`);
|
|
136
|
+
if (tools.size > 0)
|
|
137
|
+
parts.push(`- Tools used: ${[...tools].join(", ")}`);
|
|
138
|
+
if (userSnippets.length > 0) {
|
|
139
|
+
parts.push(`- First topic: ${userSnippets[0]}...`);
|
|
140
|
+
if (userSnippets.length > 1) {
|
|
141
|
+
parts.push(`- Last topic: ${userSnippets[userSnippets.length - 1]}...`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return `<context type="truncated-history">\n${parts.join("\n")}\n</context>`;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get conversation state for debugging
|
|
148
|
+
*/
|
|
149
|
+
getState(currentDiscussionId) {
|
|
150
|
+
const currentConvo = currentDiscussionId
|
|
151
|
+
? this.conversationsByDiscussion.get(currentDiscussionId)
|
|
152
|
+
: null;
|
|
153
|
+
return {
|
|
154
|
+
discussionCount: this.conversationsByDiscussion.size,
|
|
155
|
+
currentMessageCount: currentConvo?.length ?? 0,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Get full conversation for a discussion (for debugging)
|
|
160
|
+
*/
|
|
161
|
+
getFullConversation(discussionId) {
|
|
162
|
+
if (!discussionId) {
|
|
163
|
+
// Return first conversation if no ID specified
|
|
164
|
+
const first = this.conversationsByDiscussion.values().next().value;
|
|
165
|
+
return first || [];
|
|
166
|
+
}
|
|
167
|
+
return this.conversationsByDiscussion.get(discussionId) || [];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Clear all conversations
|
|
171
|
+
*/
|
|
172
|
+
clear() {
|
|
173
|
+
this.conversationsByDiscussion.clear();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Prepare conversation for API call with Anthropic prompt caching
|
|
177
|
+
* Adds cache_control to the last assistant message to cache conversation history
|
|
178
|
+
* Returns a COPY - does not modify the original conversation
|
|
179
|
+
*/
|
|
180
|
+
prepareForCaching(conversation) {
|
|
181
|
+
// Sanitize: remove any messages with empty content (prevents API 400 errors)
|
|
182
|
+
for (let i = conversation.length - 1; i >= 0; i--) {
|
|
183
|
+
const msg = conversation[i];
|
|
184
|
+
const content = msg.content;
|
|
185
|
+
const isEmpty = !content || (Array.isArray(content) && content.length === 0) ||
|
|
186
|
+
(typeof content === 'string' && content.trim() === '');
|
|
187
|
+
if (isEmpty) {
|
|
188
|
+
this.logger.warn('Removed empty content message from conversation', {
|
|
189
|
+
index: i, role: msg.role, contentType: typeof content,
|
|
190
|
+
});
|
|
191
|
+
conversation.splice(i, 1);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (conversation.length < 2) {
|
|
195
|
+
// Need at least one exchange to cache
|
|
196
|
+
return conversation;
|
|
197
|
+
}
|
|
198
|
+
// Find the last assistant message (should be second-to-last, before new user message)
|
|
199
|
+
let lastAssistantIndex = -1;
|
|
200
|
+
for (let i = conversation.length - 1; i >= 0; i--) {
|
|
201
|
+
if (conversation[i].role === "assistant") {
|
|
202
|
+
lastAssistantIndex = i;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (lastAssistantIndex === -1) {
|
|
207
|
+
// No assistant message found
|
|
208
|
+
return conversation;
|
|
209
|
+
}
|
|
210
|
+
// Shallow copy with targeted cache_control modification
|
|
211
|
+
const cached = conversation.map((msg, index) => {
|
|
212
|
+
if (index !== lastAssistantIndex) {
|
|
213
|
+
return msg;
|
|
214
|
+
}
|
|
215
|
+
// This is the last assistant message - add cache_control
|
|
216
|
+
const content = msg.content;
|
|
217
|
+
if (typeof content === "string") {
|
|
218
|
+
// Convert string to block format with cache_control
|
|
219
|
+
return {
|
|
220
|
+
role: msg.role,
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: "text",
|
|
224
|
+
text: content,
|
|
225
|
+
cache_control: { type: "ephemeral" },
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (Array.isArray(content) && content.length > 0) {
|
|
231
|
+
// Add cache_control to the last block
|
|
232
|
+
const newContent = content.map((block, blockIndex) => {
|
|
233
|
+
if (blockIndex === content.length - 1) {
|
|
234
|
+
return { ...block, cache_control: { type: "ephemeral" } };
|
|
235
|
+
}
|
|
236
|
+
return block;
|
|
237
|
+
});
|
|
238
|
+
return { role: msg.role, content: newContent };
|
|
239
|
+
}
|
|
240
|
+
return msg;
|
|
241
|
+
});
|
|
242
|
+
return cached;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
exports.ConversationManager = ConversationManager;
|
|
246
|
+
//# sourceMappingURL=conversation-manager.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { ConversationManager } from './conversation-manager';
|
|
2
|
+
export { MessageClassifier } from './message-classifier';
|
|
3
|
+
export { MessageFormatterService } from './message-formatter';
|
|
4
|
+
export { TypingIndicatorService } from './typing-indicator';
|
|
5
|
+
export { TokenBillingService } from './token-billing';
|
|
6
|
+
export { SessionLoggerService } from './session-logger';
|
|
7
|
+
export { WorkspaceSchemaCacheService } from './workspace-schema-cache';
|
|
8
|
+
export type { BotConnection, McpToolCallback } from './types';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkspaceSchemaCacheService = exports.SessionLoggerService = exports.TokenBillingService = exports.TypingIndicatorService = exports.MessageFormatterService = exports.MessageClassifier = exports.ConversationManager = void 0;
|
|
4
|
+
var conversation_manager_1 = require("./conversation-manager");
|
|
5
|
+
Object.defineProperty(exports, "ConversationManager", { enumerable: true, get: function () { return conversation_manager_1.ConversationManager; } });
|
|
6
|
+
var message_classifier_1 = require("./message-classifier");
|
|
7
|
+
Object.defineProperty(exports, "MessageClassifier", { enumerable: true, get: function () { return message_classifier_1.MessageClassifier; } });
|
|
8
|
+
var message_formatter_1 = require("./message-formatter");
|
|
9
|
+
Object.defineProperty(exports, "MessageFormatterService", { enumerable: true, get: function () { return message_formatter_1.MessageFormatterService; } });
|
|
10
|
+
var typing_indicator_1 = require("./typing-indicator");
|
|
11
|
+
Object.defineProperty(exports, "TypingIndicatorService", { enumerable: true, get: function () { return typing_indicator_1.TypingIndicatorService; } });
|
|
12
|
+
var token_billing_1 = require("./token-billing");
|
|
13
|
+
Object.defineProperty(exports, "TokenBillingService", { enumerable: true, get: function () { return token_billing_1.TokenBillingService; } });
|
|
14
|
+
var session_logger_1 = require("./session-logger");
|
|
15
|
+
Object.defineProperty(exports, "SessionLoggerService", { enumerable: true, get: function () { return session_logger_1.SessionLoggerService; } });
|
|
16
|
+
var workspace_schema_cache_1 = require("./workspace-schema-cache");
|
|
17
|
+
Object.defineProperty(exports, "WorkspaceSchemaCacheService", { enumerable: true, get: function () { return workspace_schema_cache_1.WorkspaceSchemaCacheService; } });
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Message Classifier Service
|
|
3
|
+
*
|
|
4
|
+
* Handles incoming message extraction and classification:
|
|
5
|
+
* - Extracts message content from Hailer signals
|
|
6
|
+
* - Classifies message priority (high/normal)
|
|
7
|
+
* - Detects mentions, replies, and DMs
|
|
8
|
+
*/
|
|
9
|
+
import { Logger } from "../../lib/logger";
|
|
10
|
+
import { HailerSignal } from "../../mcp/signal-handler";
|
|
11
|
+
import { BotConnection, IncomingMessage, HailerMessage } from "./types";
|
|
12
|
+
export declare class MessageClassifier {
|
|
13
|
+
private botUserId;
|
|
14
|
+
private botConnection;
|
|
15
|
+
private logger;
|
|
16
|
+
private mentionPattern;
|
|
17
|
+
constructor(botUserId: string, botConnection: BotConnection, logger: Logger);
|
|
18
|
+
/**
|
|
19
|
+
* Extract and classify incoming message from signal
|
|
20
|
+
*/
|
|
21
|
+
extractIncomingMessage(signal: HailerSignal): Promise<IncomingMessage | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Check if message mentions this bot
|
|
24
|
+
* Uses pre-compiled regex pattern for performance
|
|
25
|
+
*/
|
|
26
|
+
checkMention(content: string): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Check if message is a reply to one of our messages using pre-fetched messages.
|
|
29
|
+
* Avoids a redundant API call by reusing the messages already fetched in extractIncomingMessage.
|
|
30
|
+
*/
|
|
31
|
+
checkReplyInMessages(messages: HailerMessage[], messageId: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Check if this is a 1:1 DM with the bot using pre-loaded discussion data.
|
|
34
|
+
* Avoids a redundant API call by reusing discussion data already fetched in extractIncomingMessage.
|
|
35
|
+
*/
|
|
36
|
+
checkDirectMessageFromData(discData: any, senderId: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Check if this is a 1:1 DM with the bot (fallback - fetches discussion from API)
|
|
39
|
+
*/
|
|
40
|
+
checkDirectMessage(discussionId: string, senderId: string): Promise<boolean>;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=message-classifier.d.ts.map
|