@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,506 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Workspace Schema Cache Service
|
|
4
|
+
*
|
|
5
|
+
* Dynamically discovers and caches workflow schemas per workspace.
|
|
6
|
+
* Replaces hardcoded workflow/field IDs with runtime lookup.
|
|
7
|
+
*
|
|
8
|
+
* WORKSPACE ISOLATION: Each workspace gets its own workflow IDs.
|
|
9
|
+
* Never use hardcoded IDs - always lookup via this cache.
|
|
10
|
+
*
|
|
11
|
+
* On bot startup:
|
|
12
|
+
* 1. Check memory cache first
|
|
13
|
+
* 2. If cache miss: Query list_workflows to find AI Agent workflows by name
|
|
14
|
+
* 3. If missing, install them from marketplace
|
|
15
|
+
* 4. Query get_workflow_schema to get field IDs
|
|
16
|
+
* 5. Store in memory cache for session duration
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.WorkspaceSchemaCacheService = void 0;
|
|
20
|
+
/** AI Agent template ID in marketplace (HR for AI Agents) */
|
|
21
|
+
const AI_AGENT_TEMPLATE_ID = "685ab3c39af7445dd5384ffd";
|
|
22
|
+
/** Known workflow names from the AI Agent template (partial match, case insensitive) */
|
|
23
|
+
const TEMPLATE_WORKFLOW_PATTERNS = {
|
|
24
|
+
SESSION_LOG: ["session log", "session_log"],
|
|
25
|
+
AGENT_DIRECTORY: ["agent directory", "agent_directory"],
|
|
26
|
+
POSITIONS: ["positions", "position"],
|
|
27
|
+
TEAMS: ["teams", "team"],
|
|
28
|
+
TOOL_REGISTRY: ["tool registry", "tool_registry", "mcp server"],
|
|
29
|
+
MCP_CONFIG: ["mcp config", "mcp_config", "mcp configuration"],
|
|
30
|
+
};
|
|
31
|
+
class WorkspaceSchemaCacheService {
|
|
32
|
+
logger;
|
|
33
|
+
callMcpTool;
|
|
34
|
+
cache = new Map();
|
|
35
|
+
constructor(logger, callMcpTool) {
|
|
36
|
+
this.logger = logger;
|
|
37
|
+
this.callMcpTool = callMcpTool;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Initialize schema cache for a workspace
|
|
41
|
+
* Call this on bot startup after connecting to Hailer
|
|
42
|
+
*
|
|
43
|
+
* Set SKIP_SCHEMA_DISCOVERY=true to skip API calls on startup.
|
|
44
|
+
* HAL can discover workflows on-demand via MCP tools when needed.
|
|
45
|
+
*/
|
|
46
|
+
async initializeForWorkspace(workspaceId) {
|
|
47
|
+
// 1. Check memory cache first
|
|
48
|
+
const existing = this.cache.get(workspaceId);
|
|
49
|
+
if (existing?.initialized) {
|
|
50
|
+
this.logger.debug("Using memory-cached workspace schemas", { workspaceId });
|
|
51
|
+
return existing;
|
|
52
|
+
}
|
|
53
|
+
// 2. Check if schema discovery is disabled (saves dozens of API calls on startup)
|
|
54
|
+
if (process.env.SKIP_SCHEMA_DISCOVERY === 'true') {
|
|
55
|
+
this.logger.debug("Schema discovery skipped (SKIP_SCHEMA_DISCOVERY=true)", { workspaceId });
|
|
56
|
+
const schemas = {
|
|
57
|
+
workspaceId,
|
|
58
|
+
initialized: true,
|
|
59
|
+
};
|
|
60
|
+
this.cache.set(workspaceId, schemas);
|
|
61
|
+
return schemas;
|
|
62
|
+
}
|
|
63
|
+
// 3. Discover via API
|
|
64
|
+
this.logger.debug("Discovering workspace schemas via API", { workspaceId });
|
|
65
|
+
const schemas = {
|
|
66
|
+
workspaceId,
|
|
67
|
+
initialized: false,
|
|
68
|
+
};
|
|
69
|
+
try {
|
|
70
|
+
// Fetch all workflows in workspace
|
|
71
|
+
const workflowsResult = await this.callMcpTool("list_workflows", {
|
|
72
|
+
workspace: workspaceId,
|
|
73
|
+
});
|
|
74
|
+
const workflowsText = workflowsResult?.content?.[0]?.text;
|
|
75
|
+
if (!workflowsText) {
|
|
76
|
+
this.logger.warn("No workflows found in workspace", { workspaceId });
|
|
77
|
+
schemas.initialized = true;
|
|
78
|
+
this.cache.set(workspaceId, schemas);
|
|
79
|
+
return schemas;
|
|
80
|
+
}
|
|
81
|
+
// Parse workflows
|
|
82
|
+
const workflows = this.parseWorkflows(workflowsText);
|
|
83
|
+
this.logger.debug("Found workflows", { workspaceId, count: workflows.length });
|
|
84
|
+
// Find template workflows
|
|
85
|
+
this.mapWorkflowsToSchemas(workflows, schemas);
|
|
86
|
+
// Check if required workflows are missing
|
|
87
|
+
const missingRequired = !schemas.sessionLog || !schemas.agentDirectory;
|
|
88
|
+
if (missingRequired) {
|
|
89
|
+
this.logger.info("AI Agent workflows not found, installing from marketplace", { workspaceId });
|
|
90
|
+
const installed = await this.installAgentTemplate(workspaceId);
|
|
91
|
+
if (installed) {
|
|
92
|
+
// Re-fetch workflows after installation
|
|
93
|
+
const refreshResult = await this.callMcpTool("list_workflows", {
|
|
94
|
+
workspace: workspaceId,
|
|
95
|
+
});
|
|
96
|
+
const refreshText = refreshResult?.content?.[0]?.text;
|
|
97
|
+
if (refreshText) {
|
|
98
|
+
const refreshedWorkflows = this.parseWorkflows(refreshText);
|
|
99
|
+
this.mapWorkflowsToSchemas(refreshedWorkflows, schemas);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Load detailed schemas for found workflows
|
|
104
|
+
await this.loadDetailedSchemas(schemas);
|
|
105
|
+
schemas.initialized = true;
|
|
106
|
+
this.cache.set(workspaceId, schemas);
|
|
107
|
+
this.logger.debug("Workspace schemas cached", {
|
|
108
|
+
workspaceId,
|
|
109
|
+
hasSessionLog: !!schemas.sessionLog,
|
|
110
|
+
hasAgentDirectory: !!schemas.agentDirectory,
|
|
111
|
+
hasPositions: !!schemas.positions,
|
|
112
|
+
});
|
|
113
|
+
return schemas;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
this.logger.error("Failed to initialize workspace schemas", {
|
|
117
|
+
workspaceId,
|
|
118
|
+
error: error instanceof Error ? error.message : String(error),
|
|
119
|
+
});
|
|
120
|
+
schemas.initialized = true; // Mark as initialized even on error to prevent retry loops
|
|
121
|
+
this.cache.set(workspaceId, schemas);
|
|
122
|
+
return schemas;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get cached schemas for a workspace
|
|
127
|
+
*/
|
|
128
|
+
getSchemas(workspaceId) {
|
|
129
|
+
return this.cache.get(workspaceId);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Get Session Log schema for a workspace
|
|
133
|
+
*/
|
|
134
|
+
getSessionLogSchema(workspaceId) {
|
|
135
|
+
return this.cache.get(workspaceId)?.sessionLog;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Get Agent Directory schema for a workspace
|
|
139
|
+
*/
|
|
140
|
+
getAgentDirectorySchema(workspaceId) {
|
|
141
|
+
return this.cache.get(workspaceId)?.agentDirectory;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Get Positions schema for a workspace
|
|
145
|
+
*/
|
|
146
|
+
getPositionsSchema(workspaceId) {
|
|
147
|
+
return this.cache.get(workspaceId)?.positions;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get Teams schema for a workspace
|
|
151
|
+
*/
|
|
152
|
+
getTeamsSchema(workspaceId) {
|
|
153
|
+
return this.cache.get(workspaceId)?.teams;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Get Tool Registry schema for a workspace
|
|
157
|
+
*/
|
|
158
|
+
getToolRegistrySchema(workspaceId) {
|
|
159
|
+
return this.cache.get(workspaceId)?.toolRegistry;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Get MCP Config schema for a workspace
|
|
163
|
+
*/
|
|
164
|
+
getMcpConfigSchema(workspaceId) {
|
|
165
|
+
return this.cache.get(workspaceId)?.mcpConfig;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Check if a workspace has the AI Agent template installed
|
|
169
|
+
*/
|
|
170
|
+
hasAgentTemplate(workspaceId) {
|
|
171
|
+
const schemas = this.cache.get(workspaceId);
|
|
172
|
+
return !!(schemas?.sessionLog && schemas?.agentDirectory);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Check if workflow name matches a pattern (case insensitive, ignores emojis)
|
|
176
|
+
*/
|
|
177
|
+
matchesPattern(workflowName, patterns) {
|
|
178
|
+
// Normalize: lowercase, remove emojis and extra whitespace
|
|
179
|
+
const normalized = workflowName
|
|
180
|
+
.toLowerCase()
|
|
181
|
+
.replace(/[\u{1F300}-\u{1F9FF}]/gu, "") // Remove emojis
|
|
182
|
+
.replace(/\s+/g, " ")
|
|
183
|
+
.trim();
|
|
184
|
+
return patterns.some(pattern => normalized.includes(pattern));
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Map workflow list to schema slots (using pattern matching)
|
|
188
|
+
*/
|
|
189
|
+
mapWorkflowsToSchemas(workflows, schemas) {
|
|
190
|
+
for (const workflow of workflows) {
|
|
191
|
+
// Filter by workspace if specified
|
|
192
|
+
if (schemas.workspaceId && workflow.workspaceId && workflow.workspaceId !== schemas.workspaceId) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (this.matchesPattern(workflow.name, TEMPLATE_WORKFLOW_PATTERNS.SESSION_LOG)) {
|
|
196
|
+
schemas.sessionLog = { workflowId: workflow.id, name: workflow.name, phases: {}, fields: {} };
|
|
197
|
+
}
|
|
198
|
+
else if (this.matchesPattern(workflow.name, TEMPLATE_WORKFLOW_PATTERNS.AGENT_DIRECTORY)) {
|
|
199
|
+
schemas.agentDirectory = { workflowId: workflow.id, name: workflow.name, phases: {}, fields: {} };
|
|
200
|
+
}
|
|
201
|
+
else if (this.matchesPattern(workflow.name, TEMPLATE_WORKFLOW_PATTERNS.POSITIONS)) {
|
|
202
|
+
schemas.positions = { workflowId: workflow.id, name: workflow.name, phases: {}, fields: {} };
|
|
203
|
+
}
|
|
204
|
+
else if (this.matchesPattern(workflow.name, TEMPLATE_WORKFLOW_PATTERNS.TEAMS) && workflow.name.toLowerCase().includes("team")) {
|
|
205
|
+
// Be more specific for Teams to avoid matching "Teams" workflow in sports data
|
|
206
|
+
if (workflow.name.includes("\uD83C\uDF96\uFE0F") || workflow.name.toLowerCase() === "teams") {
|
|
207
|
+
schemas.teams = { workflowId: workflow.id, name: workflow.name, phases: {}, fields: {} };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
else if (this.matchesPattern(workflow.name, TEMPLATE_WORKFLOW_PATTERNS.TOOL_REGISTRY)) {
|
|
211
|
+
schemas.toolRegistry = { workflowId: workflow.id, name: workflow.name, phases: {}, fields: {} };
|
|
212
|
+
}
|
|
213
|
+
else if (this.matchesPattern(workflow.name, TEMPLATE_WORKFLOW_PATTERNS.MCP_CONFIG)) {
|
|
214
|
+
schemas.mcpConfig = { workflowId: workflow.id, name: workflow.name, phases: {}, fields: {} };
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Load detailed schemas (phases, fields) for all found workflows
|
|
220
|
+
*/
|
|
221
|
+
async loadDetailedSchemas(schemas) {
|
|
222
|
+
const schemaProps = ['sessionLog', 'agentDirectory', 'positions', 'teams', 'toolRegistry', 'mcpConfig'];
|
|
223
|
+
for (const prop of schemaProps) {
|
|
224
|
+
if (schemas[prop]) {
|
|
225
|
+
await this.loadWorkflowSchemaDetails(schemas[prop]);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Install AI Agent template from marketplace
|
|
231
|
+
*/
|
|
232
|
+
async installAgentTemplate(workspaceId) {
|
|
233
|
+
try {
|
|
234
|
+
// First try to find the template by name in marketplace
|
|
235
|
+
const templatesResult = await this.callMcpTool("list_templates", {
|
|
236
|
+
publicOnly: true,
|
|
237
|
+
});
|
|
238
|
+
let templateId = AI_AGENT_TEMPLATE_ID;
|
|
239
|
+
const templatesText = templatesResult?.content?.[0]?.text;
|
|
240
|
+
if (templatesText) {
|
|
241
|
+
try {
|
|
242
|
+
const parsed = JSON.parse(templatesText);
|
|
243
|
+
const templates = parsed.templates || parsed.data || parsed;
|
|
244
|
+
if (Array.isArray(templates)) {
|
|
245
|
+
const agentTemplate = templates.find((t) => t.name?.includes("AI Agent") || t.title?.includes("AI Agent"));
|
|
246
|
+
if (agentTemplate?._id) {
|
|
247
|
+
templateId = agentTemplate._id;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// Use default template ID
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
this.logger.info("Installing AI Agent template", { workspaceId, templateId });
|
|
256
|
+
const installResult = await this.callMcpTool("install_template", {
|
|
257
|
+
templateId,
|
|
258
|
+
workspaceId,
|
|
259
|
+
});
|
|
260
|
+
const installText = installResult?.content?.[0]?.text || "";
|
|
261
|
+
// Check for explicit error indicators
|
|
262
|
+
const hasError = installText.toLowerCase().includes("error") ||
|
|
263
|
+
installText.toLowerCase().includes("not found") ||
|
|
264
|
+
installText.toLowerCase().includes("failed");
|
|
265
|
+
const hasSuccess = installText.toLowerCase().includes("success") ||
|
|
266
|
+
installText.toLowerCase().includes("installed") ||
|
|
267
|
+
installText.toLowerCase().includes("created");
|
|
268
|
+
const success = hasSuccess && !hasError;
|
|
269
|
+
if (success) {
|
|
270
|
+
this.logger.info("AI Agent template installed successfully", { workspaceId });
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
this.logger.warn("Template installation failed", {
|
|
274
|
+
workspaceId,
|
|
275
|
+
response: installText.substring(0, 300),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
return success;
|
|
279
|
+
}
|
|
280
|
+
catch (error) {
|
|
281
|
+
this.logger.error("Failed to install AI Agent template", {
|
|
282
|
+
workspaceId,
|
|
283
|
+
error: error instanceof Error ? error.message : String(error),
|
|
284
|
+
});
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Parse workflows from list_workflows response
|
|
290
|
+
* Handles both raw JSON and markdown-wrapped JSON responses
|
|
291
|
+
*/
|
|
292
|
+
parseWorkflows(text) {
|
|
293
|
+
const workflows = [];
|
|
294
|
+
try {
|
|
295
|
+
// First, try to extract JSON array from markdown-wrapped response
|
|
296
|
+
let jsonText = text;
|
|
297
|
+
// Try to extract JSON array from markdown
|
|
298
|
+
const jsonArrayMatch = text.match(/\[\s*\{[\s\S]*?\}\s*\]/);
|
|
299
|
+
if (jsonArrayMatch) {
|
|
300
|
+
jsonText = jsonArrayMatch[0];
|
|
301
|
+
}
|
|
302
|
+
const parsed = JSON.parse(jsonText);
|
|
303
|
+
const items = Array.isArray(parsed) ? parsed : (parsed.workflows || parsed.data || []);
|
|
304
|
+
if (Array.isArray(items)) {
|
|
305
|
+
for (const item of items) {
|
|
306
|
+
// Support both `id` and `_id` field names
|
|
307
|
+
const id = item.id || item._id;
|
|
308
|
+
const name = item.name;
|
|
309
|
+
const workspaceId = item.workspaceId;
|
|
310
|
+
if (id && name) {
|
|
311
|
+
workflows.push({ id, name, workspaceId });
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
// Fallback: regex extraction for both id and _id patterns
|
|
318
|
+
const idMatches = text.matchAll(/"(?:id|_id)":\s*"([a-f0-9]{24})"/gi);
|
|
319
|
+
const nameMatches = text.matchAll(/"name":\s*"([^"]+)"/gi);
|
|
320
|
+
const wsMatches = text.matchAll(/"workspaceId":\s*"([a-f0-9]{24})"/gi);
|
|
321
|
+
const ids = [...idMatches].map(m => m[1]);
|
|
322
|
+
const names = [...nameMatches].map(m => m[1]);
|
|
323
|
+
const wsIds = [...wsMatches].map(m => m[1]);
|
|
324
|
+
for (let i = 0; i < Math.min(ids.length, names.length); i++) {
|
|
325
|
+
workflows.push({
|
|
326
|
+
id: ids[i],
|
|
327
|
+
name: names[i],
|
|
328
|
+
workspaceId: wsIds[i],
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
this.logger.debug("Parsed workflows from response", { count: workflows.length });
|
|
333
|
+
return workflows;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Load detailed workflow schema (phases and fields) for an existing schema
|
|
337
|
+
*/
|
|
338
|
+
async loadWorkflowSchemaDetails(schema) {
|
|
339
|
+
try {
|
|
340
|
+
// Get phases first
|
|
341
|
+
const phasesResult = await this.callMcpTool("list_workflow_phases", {
|
|
342
|
+
workflowId: schema.workflowId,
|
|
343
|
+
});
|
|
344
|
+
const phasesText = phasesResult?.content?.[0]?.text;
|
|
345
|
+
if (phasesText) {
|
|
346
|
+
this.parsePhases(phasesText, schema);
|
|
347
|
+
}
|
|
348
|
+
this.logger.debug("Parsed phases for workflow", {
|
|
349
|
+
workflowId: schema.workflowId,
|
|
350
|
+
workflowName: schema.name,
|
|
351
|
+
phaseCount: Object.keys(schema.phases).length,
|
|
352
|
+
phases: Object.keys(schema.phases),
|
|
353
|
+
});
|
|
354
|
+
// Get schema with fields (use first phase)
|
|
355
|
+
const firstPhaseId = Object.values(schema.phases)[0];
|
|
356
|
+
if (firstPhaseId) {
|
|
357
|
+
const schemaResult = await this.callMcpTool("get_workflow_schema", {
|
|
358
|
+
workflowId: schema.workflowId,
|
|
359
|
+
phaseId: firstPhaseId,
|
|
360
|
+
});
|
|
361
|
+
const schemaText = schemaResult?.content?.[0]?.text;
|
|
362
|
+
if (schemaText) {
|
|
363
|
+
this.parseFields(schemaText, schema);
|
|
364
|
+
this.logger.debug("Parsed fields for workflow", {
|
|
365
|
+
workflowId: schema.workflowId,
|
|
366
|
+
workflowName: schema.name,
|
|
367
|
+
fieldCount: Object.keys(schema.fields).length,
|
|
368
|
+
fieldKeys: Object.keys(schema.fields).slice(0, 10), // First 10 keys
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
this.logger.warn("No schema text returned from get_workflow_schema", {
|
|
373
|
+
workflowId: schema.workflowId,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
this.logger.warn("No phase ID found - skipping field schema load", {
|
|
379
|
+
workflowId: schema.workflowId,
|
|
380
|
+
workflowName: schema.name,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
this.logger.warn("Failed to load workflow schema details", {
|
|
386
|
+
workflowId: schema.workflowId,
|
|
387
|
+
workflowName: schema.name,
|
|
388
|
+
error: error instanceof Error ? error.message : String(error),
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Parse phases from list_workflow_phases response
|
|
394
|
+
* Format: 1. **PhaseName**\n - Phase ID: `phaseId`
|
|
395
|
+
*/
|
|
396
|
+
parsePhases(text, schema) {
|
|
397
|
+
this.logger.debug("Parsing phases text", {
|
|
398
|
+
workflowId: schema.workflowId,
|
|
399
|
+
textLength: text.length,
|
|
400
|
+
textPreview: text.substring(0, 500),
|
|
401
|
+
});
|
|
402
|
+
try {
|
|
403
|
+
const parsed = JSON.parse(text);
|
|
404
|
+
const phases = parsed.phases || parsed.data || parsed;
|
|
405
|
+
if (Array.isArray(phases)) {
|
|
406
|
+
for (const phase of phases) {
|
|
407
|
+
if (phase.id && phase.name) {
|
|
408
|
+
schema.phases[phase.name.toLowerCase()] = phase.id;
|
|
409
|
+
}
|
|
410
|
+
else if (phase._id && phase.name) {
|
|
411
|
+
schema.phases[phase.name.toLowerCase()] = phase._id;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
// Markdown format: **PhaseName**\n - Phase ID: `phaseId`
|
|
418
|
+
// Match pattern: \d+\. **Name** ... Phase ID: `id`
|
|
419
|
+
const phaseBlocks = text.split(/\d+\.\s*\*\*/).filter(Boolean);
|
|
420
|
+
for (const block of phaseBlocks) {
|
|
421
|
+
const nameMatch = block.match(/^([^*]+)\*\*/);
|
|
422
|
+
const phaseIdMatch = block.match(/Phase ID:\s*`([a-f0-9]{24})`/i);
|
|
423
|
+
if (nameMatch && phaseIdMatch) {
|
|
424
|
+
const phaseName = nameMatch[1].trim();
|
|
425
|
+
const phaseId = phaseIdMatch[1];
|
|
426
|
+
schema.phases[phaseName.toLowerCase()] = phaseId;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* Parse fields from get_workflow_schema response
|
|
433
|
+
* Stores fields by both label and key for flexible lookup
|
|
434
|
+
*/
|
|
435
|
+
parseFields(text, schema) {
|
|
436
|
+
try {
|
|
437
|
+
const parsed = JSON.parse(text);
|
|
438
|
+
const fields = parsed.fields || parsed.data || parsed;
|
|
439
|
+
if (Array.isArray(fields)) {
|
|
440
|
+
for (const field of fields) {
|
|
441
|
+
const fieldId = field.id || field._id || field.fieldId;
|
|
442
|
+
const label = field.label || field.name;
|
|
443
|
+
const key = field.key;
|
|
444
|
+
if (fieldId && label) {
|
|
445
|
+
schema.fields[label] = fieldId;
|
|
446
|
+
}
|
|
447
|
+
// Also store by key for direct lookup (e.g., "firstName" -> fieldId)
|
|
448
|
+
if (fieldId && key) {
|
|
449
|
+
schema.fields[key] = fieldId;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
else if (typeof fields === "object") {
|
|
454
|
+
// Handle object format {fieldId: {label, type, key, ...}}
|
|
455
|
+
for (const [fieldId, fieldData] of Object.entries(fields)) {
|
|
456
|
+
const data = fieldData;
|
|
457
|
+
const label = data.label || data.name;
|
|
458
|
+
const key = data.key;
|
|
459
|
+
if (label) {
|
|
460
|
+
schema.fields[label] = fieldId;
|
|
461
|
+
}
|
|
462
|
+
// Also store by key
|
|
463
|
+
if (key) {
|
|
464
|
+
schema.fields[key] = fieldId;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
catch {
|
|
470
|
+
// Regex fallback for markdown format:
|
|
471
|
+
// **Label**
|
|
472
|
+
// - Field ID: `fieldId`
|
|
473
|
+
// - Key: `key`
|
|
474
|
+
const fieldBlocks = text.split(/\d+\.\s*\*\*/).filter(Boolean);
|
|
475
|
+
for (const block of fieldBlocks) {
|
|
476
|
+
const labelMatch = block.match(/^([^*\n]+)\*\*/);
|
|
477
|
+
const fieldIdMatch = block.match(/Field ID:\s*`([a-f0-9]{24})`/i);
|
|
478
|
+
const keyMatch = block.match(/Key:\s*`(\w+)`/i);
|
|
479
|
+
if (fieldIdMatch) {
|
|
480
|
+
const fieldId = fieldIdMatch[1];
|
|
481
|
+
if (labelMatch) {
|
|
482
|
+
schema.fields[labelMatch[1].trim()] = fieldId;
|
|
483
|
+
}
|
|
484
|
+
if (keyMatch) {
|
|
485
|
+
schema.fields[keyMatch[1]] = fieldId;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Clear cache for a workspace (memory cache only)
|
|
493
|
+
*/
|
|
494
|
+
clearCache(workspaceId) {
|
|
495
|
+
if (workspaceId) {
|
|
496
|
+
this.cache.delete(workspaceId);
|
|
497
|
+
this.logger.debug("Cleared memory cache", { workspaceId });
|
|
498
|
+
}
|
|
499
|
+
else {
|
|
500
|
+
this.cache.clear();
|
|
501
|
+
this.logger.debug("Cleared all memory caches");
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
exports.WorkspaceSchemaCacheService = WorkspaceSchemaCacheService;
|
|
506
|
+
//# sourceMappingURL=workspace-schema-cache.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolExecutor - Thin wrapper around ToolRegistry for direct tool execution.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the old McpClientService which did HTTP POST to localhost:3030.
|
|
5
|
+
* This calls ToolRegistry.executeTool() directly - no HTTP, no JSON-RPC.
|
|
6
|
+
*/
|
|
7
|
+
import { ToolRegistry, ToolGroup, ToolDefinition } from '../mcp/tool-registry';
|
|
8
|
+
import { UserContext } from '../mcp/UserContextCache';
|
|
9
|
+
export declare class ToolExecutor {
|
|
10
|
+
private toolRegistry;
|
|
11
|
+
constructor(toolRegistry: ToolRegistry);
|
|
12
|
+
/**
|
|
13
|
+
* Execute a tool by name with the given arguments and user context.
|
|
14
|
+
*/
|
|
15
|
+
execute(toolName: string, args: Record<string, unknown>, userContext: UserContext): Promise<any>;
|
|
16
|
+
/**
|
|
17
|
+
* Get tool definitions, optionally filtered by group or tool name whitelist.
|
|
18
|
+
*/
|
|
19
|
+
getToolDefinitions(filter?: {
|
|
20
|
+
allowedGroups?: ToolGroup[];
|
|
21
|
+
allowedTools?: string[];
|
|
22
|
+
}): ToolDefinition[];
|
|
23
|
+
/**
|
|
24
|
+
* Get total number of registered tools.
|
|
25
|
+
*/
|
|
26
|
+
getToolCount(): number;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=tool-executor.d.ts.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ToolExecutor - Thin wrapper around ToolRegistry for direct tool execution.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the old McpClientService which did HTTP POST to localhost:3030.
|
|
6
|
+
* This calls ToolRegistry.executeTool() directly - no HTTP, no JSON-RPC.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ToolExecutor = void 0;
|
|
10
|
+
const logger_1 = require("../lib/logger");
|
|
11
|
+
const logger = (0, logger_1.createLogger)({ component: 'tool-executor' });
|
|
12
|
+
class ToolExecutor {
|
|
13
|
+
toolRegistry;
|
|
14
|
+
constructor(toolRegistry) {
|
|
15
|
+
this.toolRegistry = toolRegistry;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Execute a tool by name with the given arguments and user context.
|
|
19
|
+
*/
|
|
20
|
+
async execute(toolName, args, userContext) {
|
|
21
|
+
logger.debug('Executing tool', { toolName });
|
|
22
|
+
try {
|
|
23
|
+
const result = await this.toolRegistry.executeTool(toolName, args, userContext);
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
logger.error('Tool execution failed', {
|
|
28
|
+
toolName,
|
|
29
|
+
error: error instanceof Error ? error.message : String(error),
|
|
30
|
+
});
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get tool definitions, optionally filtered by group or tool name whitelist.
|
|
36
|
+
*/
|
|
37
|
+
getToolDefinitions(filter) {
|
|
38
|
+
return this.toolRegistry.getToolDefinitions(filter);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get total number of registered tools.
|
|
42
|
+
*/
|
|
43
|
+
getToolCount() {
|
|
44
|
+
return this.toolRegistry.getToolCount();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.ToolExecutor = ToolExecutor;
|
|
48
|
+
//# sourceMappingURL=tool-executor.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { HailerV2CoreInitResponse } from '../mcp/utils/index';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a compact XML workspace overview for the bot's system prompt.
|
|
4
|
+
* Tiered approach - includes just enough for the LLM to know what exists:
|
|
5
|
+
* - Workflow names, IDs, activity counts, phases
|
|
6
|
+
* - NO field details (LLM uses get_workflow_schema tool on-demand)
|
|
7
|
+
* - User count only (LLM uses search_workspace_users tool on-demand)
|
|
8
|
+
* - Team names and IDs
|
|
9
|
+
* This keeps the system prompt small regardless of workspace size.
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateWorkspaceOverview(init: HailerV2CoreInitResponse): string;
|
|
12
|
+
//# sourceMappingURL=workspace-overview.d.ts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateWorkspaceOverview = generateWorkspaceOverview;
|
|
4
|
+
/**
|
|
5
|
+
* Escapes XML special characters in a string.
|
|
6
|
+
*/
|
|
7
|
+
function escapeXml(str) {
|
|
8
|
+
return str
|
|
9
|
+
.replace(/&/g, '&')
|
|
10
|
+
.replace(/</g, '<')
|
|
11
|
+
.replace(/>/g, '>')
|
|
12
|
+
.replace(/"/g, '"')
|
|
13
|
+
.replace(/'/g, ''');
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Builds an XML attribute string from key-value pairs, escaping values.
|
|
17
|
+
*/
|
|
18
|
+
function attrs(pairs) {
|
|
19
|
+
return Object.entries(pairs)
|
|
20
|
+
.map(([k, v]) => `${k}="${escapeXml(String(v))}"`)
|
|
21
|
+
.join(' ');
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generates a compact XML workspace overview for the bot's system prompt.
|
|
25
|
+
* Tiered approach - includes just enough for the LLM to know what exists:
|
|
26
|
+
* - Workflow names, IDs, activity counts, phases
|
|
27
|
+
* - NO field details (LLM uses get_workflow_schema tool on-demand)
|
|
28
|
+
* - User count only (LLM uses search_workspace_users tool on-demand)
|
|
29
|
+
* - Team names and IDs
|
|
30
|
+
* This keeps the system prompt small regardless of workspace size.
|
|
31
|
+
*/
|
|
32
|
+
function generateWorkspaceOverview(init) {
|
|
33
|
+
const lines = [];
|
|
34
|
+
// Resolve workspace name and ID
|
|
35
|
+
const network = init.network;
|
|
36
|
+
const networks = (init.networks || {});
|
|
37
|
+
const wsId = network?._id || Object.keys(networks)[0] || 'unknown';
|
|
38
|
+
const wsName = network?.name || networks[wsId]?.name || 'Workspace';
|
|
39
|
+
const userCount = Object.keys(init.users || {}).length;
|
|
40
|
+
lines.push(`<workspace ${attrs({ name: wsName, id: wsId, users: userCount })}>`);
|
|
41
|
+
// Workflows - names, IDs, activity counts, and phases only (no fields)
|
|
42
|
+
const processes = init.processes || [];
|
|
43
|
+
if (processes.length > 0) {
|
|
44
|
+
lines.push(' <workflows>');
|
|
45
|
+
for (const proc of processes) {
|
|
46
|
+
const activityCount = proc.createdActivities ?? 0;
|
|
47
|
+
const fieldCount = proc.fieldsOrder?.length || Object.keys(proc.fields || {}).length;
|
|
48
|
+
lines.push(` <workflow ${attrs({ name: proc.name, id: proc._id, activities: activityCount, fields: fieldCount })}>`);
|
|
49
|
+
// Phases (compact - always useful for the LLM to know)
|
|
50
|
+
const phases = proc.phases || {};
|
|
51
|
+
const phaseOrder = proc.phasesOrder || Object.keys(phases);
|
|
52
|
+
if (phaseOrder.length > 0) {
|
|
53
|
+
lines.push(' <phases>');
|
|
54
|
+
for (const phaseId of phaseOrder) {
|
|
55
|
+
const phase = phases[phaseId];
|
|
56
|
+
if (!phase)
|
|
57
|
+
continue;
|
|
58
|
+
lines.push(` <phase ${attrs({ name: phase.name, id: phaseId })}/>`);
|
|
59
|
+
}
|
|
60
|
+
lines.push(' </phases>');
|
|
61
|
+
}
|
|
62
|
+
lines.push(' </workflow>');
|
|
63
|
+
}
|
|
64
|
+
lines.push(' </workflows>');
|
|
65
|
+
}
|
|
66
|
+
// Teams (compact - just names and IDs)
|
|
67
|
+
const teams = init.teams;
|
|
68
|
+
if (teams) {
|
|
69
|
+
const teamEntries = Array.isArray(teams)
|
|
70
|
+
? teams
|
|
71
|
+
: Object.values(teams);
|
|
72
|
+
if (teamEntries.length > 0) {
|
|
73
|
+
lines.push(' <teams>');
|
|
74
|
+
for (const team of teamEntries) {
|
|
75
|
+
if (!team || !team.name)
|
|
76
|
+
continue;
|
|
77
|
+
const teamAttrs = {
|
|
78
|
+
name: team.name,
|
|
79
|
+
id: team._id || 'unknown',
|
|
80
|
+
};
|
|
81
|
+
if (Array.isArray(team.members)) {
|
|
82
|
+
teamAttrs.memberCount = team.members.length;
|
|
83
|
+
}
|
|
84
|
+
lines.push(` <team ${attrs(teamAttrs)}/>`);
|
|
85
|
+
}
|
|
86
|
+
lines.push(' </teams>');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// No individual users listed - LLM uses search_workspace_users tool
|
|
90
|
+
// No field details listed - LLM uses get_workflow_schema tool
|
|
91
|
+
lines.push('</workspace>');
|
|
92
|
+
return lines.join('\n');
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=workspace-overview.js.map
|