coaiajs 0.1.0
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.md +56 -0
- package/KINSHIP.md +63 -0
- package/README.md +148 -0
- package/articles/academic/creative-orientation-vs-problem-solving.md +177 -0
- package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +142 -0
- package/articles/academic/langfuse-observability-llm-pipelines.md +144 -0
- package/articles/academic/medicine-wheel-software-architecture.md +163 -0
- package/articles/academic/mmot-autonomous-agents.md +156 -0
- package/articles/academic/model-context-protocol-interagent.md +161 -0
- package/articles/academic/pde-prompt-decomposition.md +186 -0
- package/articles/academic/structural-tension-in-ai-agents.md +134 -0
- package/articles/reviews/mcp-protocol-design-review.md +170 -0
- package/articles/reviews/observability-ai-systems-review.md +176 -0
- package/articles/reviews/prompt-engineering-decomposition-review.md +184 -0
- package/articles/surveys/agent-orchestration-survey.md +186 -0
- package/articles/surveys/knowledge-graph-storage-survey.md +204 -0
- package/articles/surveys/structural-tension-methodology-survey.md +154 -0
- package/articles/technical/aws-sdk-v3-polly.md +270 -0
- package/articles/technical/commander-cli-framework.md +262 -0
- package/articles/technical/dotenv-config-patterns.md +360 -0
- package/articles/technical/ioredis-vs-redis.md +142 -0
- package/articles/technical/langfuse-js-sdk-vs-rest.md +191 -0
- package/articles/technical/mcp-sdk-typescript.md +291 -0
- package/articles/technical/octokit-github-api.md +293 -0
- package/articles/technical/openai-sdk-modern.md +231 -0
- package/articles/technical/yaml-parsing-node.md +266 -0
- package/articles/technical/zod-runtime-validation.md +212 -0
- package/dist/mcp/config.d.ts +14 -0
- package/dist/mcp/config.js +185 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +324 -0
- package/dist/mcp/tools/coaiapy-tools.d.ts +8 -0
- package/dist/mcp/tools/coaiapy-tools.js +326 -0
- package/dist/mcp/tools/index.d.ts +3 -0
- package/dist/mcp/tools/index.js +3 -0
- package/dist/src/audio.d.ts +12 -0
- package/dist/src/audio.js +57 -0
- package/dist/src/cli-helpers.d.ts +9 -0
- package/dist/src/cli-helpers.js +78 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.js +1086 -0
- package/dist/src/config.d.ts +15 -0
- package/dist/src/config.js +145 -0
- package/dist/src/environment.d.ts +44 -0
- package/dist/src/environment.js +146 -0
- package/dist/src/github.d.ts +47 -0
- package/dist/src/github.js +79 -0
- package/dist/src/langfuse/client.d.ts +30 -0
- package/dist/src/langfuse/client.js +75 -0
- package/dist/src/langfuse/comments.d.ts +16 -0
- package/dist/src/langfuse/comments.js +36 -0
- package/dist/src/langfuse/datasets.d.ts +19 -0
- package/dist/src/langfuse/datasets.js +140 -0
- package/dist/src/langfuse/index.d.ts +13 -0
- package/dist/src/langfuse/index.js +10 -0
- package/dist/src/langfuse/media.d.ts +11 -0
- package/dist/src/langfuse/media.js +167 -0
- package/dist/src/langfuse/observations.d.ts +18 -0
- package/dist/src/langfuse/observations.js +113 -0
- package/dist/src/langfuse/prompts.d.ts +14 -0
- package/dist/src/langfuse/prompts.js +127 -0
- package/dist/src/langfuse/scores.d.ts +54 -0
- package/dist/src/langfuse/scores.js +366 -0
- package/dist/src/langfuse/traces.d.ts +29 -0
- package/dist/src/langfuse/traces.js +225 -0
- package/dist/src/llm.d.ts +23 -0
- package/dist/src/llm.js +76 -0
- package/dist/src/narrative/graph-manager.d.ts +106 -0
- package/dist/src/narrative/graph-manager.js +915 -0
- package/dist/src/narrative/index.d.ts +9 -0
- package/dist/src/narrative/index.js +7 -0
- package/dist/src/narrative/markdown-export.d.ts +21 -0
- package/dist/src/narrative/markdown-export.js +383 -0
- package/dist/src/narrative/tool-definitions.d.ts +21 -0
- package/dist/src/narrative/tool-definitions.js +588 -0
- package/dist/src/narrative/tool-handlers.d.ts +11 -0
- package/dist/src/narrative/tool-handlers.js +462 -0
- package/dist/src/narrative/types.d.ts +2 -0
- package/dist/src/narrative/types.js +2 -0
- package/dist/src/narrative/validation.d.ts +29 -0
- package/dist/src/narrative/validation.js +144 -0
- package/dist/src/pde/index.d.ts +8 -0
- package/dist/src/pde/index.js +8 -0
- package/dist/src/pde/mcp-handlers.d.ts +7 -0
- package/dist/src/pde/mcp-handlers.js +300 -0
- package/dist/src/pde/mcp-tools.d.ts +15 -0
- package/dist/src/pde/mcp-tools.js +191 -0
- package/dist/src/pde/session-manager.d.ts +32 -0
- package/dist/src/pde/session-manager.js +205 -0
- package/dist/src/pde/stc-mapper.d.ts +26 -0
- package/dist/src/pde/stc-mapper.js +234 -0
- package/dist/src/pipeline/index.d.ts +2 -0
- package/dist/src/pipeline/index.js +3 -0
- package/dist/src/pipeline/template-engine.d.ts +20 -0
- package/dist/src/pipeline/template-engine.js +361 -0
- package/dist/src/planning/index.d.ts +7 -0
- package/dist/src/planning/index.js +7 -0
- package/dist/src/planning/mcp-handlers.d.ts +7 -0
- package/dist/src/planning/mcp-handlers.js +310 -0
- package/dist/src/planning/mcp-tools.d.ts +15 -0
- package/dist/src/planning/mcp-tools.js +145 -0
- package/dist/src/planning/plan-parser.d.ts +35 -0
- package/dist/src/planning/plan-parser.js +506 -0
- package/dist/src/redis.d.ts +21 -0
- package/dist/src/redis.js +78 -0
- package/dist/src/types.d.ts +244 -0
- package/dist/src/types.js +8 -0
- package/mcp/config.ts +196 -0
- package/mcp/server.ts +402 -0
- package/mcp/tools/coaiapy-tools.ts +364 -0
- package/mcp/tools/index.ts +4 -0
- package/package.json +52 -0
- package/rispecs/00-coaiajs-platform.spec.md +102 -0
- package/rispecs/01-core-config.spec.md +89 -0
- package/rispecs/02-redis-module.spec.md +62 -0
- package/rispecs/03-langfuse-module.spec.md +101 -0
- package/rispecs/04-narrative-engine.spec.md +185 -0
- package/rispecs/05-pde-engine.spec.md +112 -0
- package/rispecs/06-planning-engine.spec.md +128 -0
- package/rispecs/07-pipeline-templates.spec.md +97 -0
- package/rispecs/08-cli-interface.spec.md +134 -0
- package/rispecs/09-mcp-server.spec.md +140 -0
- package/rispecs/10-audio-module.spec.md +63 -0
- package/rispecs/KINSHIP.md +56 -0
- package/rispecs/README.md +100 -0
- package/src/audio.ts +76 -0
- package/src/cli-helpers.ts +86 -0
- package/src/cli.ts +1223 -0
- package/src/config.ts +172 -0
- package/src/environment.ts +171 -0
- package/src/github.ts +143 -0
- package/src/langfuse/client.ts +105 -0
- package/src/langfuse/comments.ts +52 -0
- package/src/langfuse/datasets.ts +178 -0
- package/src/langfuse/index.ts +33 -0
- package/src/langfuse/media.ts +193 -0
- package/src/langfuse/observations.ts +131 -0
- package/src/langfuse/prompts.ts +157 -0
- package/src/langfuse/scores.ts +456 -0
- package/src/langfuse/traces.ts +276 -0
- package/src/llm.ts +106 -0
- package/src/narrative/graph-manager.ts +1358 -0
- package/src/narrative/index.ts +32 -0
- package/src/narrative/markdown-export.ts +535 -0
- package/src/narrative/tool-definitions.ts +635 -0
- package/src/narrative/tool-handlers.ts +528 -0
- package/src/narrative/types.ts +9 -0
- package/src/narrative/validation.ts +179 -0
- package/src/pde/index.ts +8 -0
- package/src/pde/mcp-handlers.ts +359 -0
- package/src/pde/mcp-tools.ts +201 -0
- package/src/pde/session-manager.ts +248 -0
- package/src/pde/stc-mapper.ts +298 -0
- package/src/pipeline/index.ts +7 -0
- package/src/pipeline/template-engine.ts +398 -0
- package/src/planning/index.ts +13 -0
- package/src/planning/mcp-handlers.ts +369 -0
- package/src/planning/mcp-tools.ts +155 -0
- package/src/planning/plan-parser.ts +587 -0
- package/src/redis.ts +97 -0
- package/src/types.ts +280 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Strict input validation for multi-LLM compatibility (no zod dependency)
|
|
2
|
+
type ValidationType = 'string' | 'number' | 'boolean' | 'array' | 'object' | 'date' | 'enum';
|
|
3
|
+
|
|
4
|
+
interface ValidationRule {
|
|
5
|
+
type: ValidationType;
|
|
6
|
+
required?: boolean;
|
|
7
|
+
minLength?: number;
|
|
8
|
+
maxLength?: number;
|
|
9
|
+
pattern?: RegExp;
|
|
10
|
+
minValue?: number;
|
|
11
|
+
maxValue?: number;
|
|
12
|
+
enumValues?: (string | number)[];
|
|
13
|
+
items?: ValidationRule;
|
|
14
|
+
properties?: Record<string, ValidationRule>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ValidationSchema {
|
|
18
|
+
[key: string]: ValidationRule;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function validate(args: unknown, schema: ValidationSchema): { valid: boolean; error?: string } {
|
|
22
|
+
if (typeof args !== 'object' || args === null) {
|
|
23
|
+
return { valid: false, error: 'Arguments must be an object' };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const record = args as Record<string, unknown>;
|
|
27
|
+
|
|
28
|
+
for (const [key, rule] of Object.entries(schema)) {
|
|
29
|
+
const value = record[key];
|
|
30
|
+
|
|
31
|
+
if (rule.required && (value === undefined || value === null)) {
|
|
32
|
+
return { valid: false, error: `Missing required field: ${key}` };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (value === undefined || value === null) continue;
|
|
36
|
+
|
|
37
|
+
const result = validateValue(value, rule, key);
|
|
38
|
+
if (!result.valid) return result;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { valid: true };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validateValue(value: unknown, rule: ValidationRule, path: string): { valid: boolean; error?: string } {
|
|
45
|
+
switch (rule.type) {
|
|
46
|
+
case 'string':
|
|
47
|
+
if (typeof value !== 'string') {
|
|
48
|
+
return { valid: false, error: `${path} must be a string, got ${typeof value}` };
|
|
49
|
+
}
|
|
50
|
+
if (rule.minLength && value.length < rule.minLength) {
|
|
51
|
+
return { valid: false, error: `${path} must be at least ${rule.minLength} characters` };
|
|
52
|
+
}
|
|
53
|
+
if (rule.maxLength && value.length > rule.maxLength) {
|
|
54
|
+
return { valid: false, error: `${path} must be at most ${rule.maxLength} characters` };
|
|
55
|
+
}
|
|
56
|
+
if (rule.pattern && !rule.pattern.test(value)) {
|
|
57
|
+
return { valid: false, error: `${path} format is invalid` };
|
|
58
|
+
}
|
|
59
|
+
if (rule.enumValues && !rule.enumValues.includes(value)) {
|
|
60
|
+
return { valid: false, error: `${path} must be one of: ${rule.enumValues.join(', ')}` };
|
|
61
|
+
}
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'number':
|
|
65
|
+
if (typeof value !== 'number' || isNaN(value)) {
|
|
66
|
+
return { valid: false, error: `${path} must be a number` };
|
|
67
|
+
}
|
|
68
|
+
if (rule.minValue !== undefined && value < rule.minValue) {
|
|
69
|
+
return { valid: false, error: `${path} must be at least ${rule.minValue}` };
|
|
70
|
+
}
|
|
71
|
+
if (rule.maxValue !== undefined && value > rule.maxValue) {
|
|
72
|
+
return { valid: false, error: `${path} must be at most ${rule.maxValue}` };
|
|
73
|
+
}
|
|
74
|
+
if (rule.enumValues && !rule.enumValues.includes(value)) {
|
|
75
|
+
return { valid: false, error: `${path} must be one of: ${rule.enumValues.join(', ')}` };
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
|
|
79
|
+
case 'boolean':
|
|
80
|
+
if (typeof value !== 'boolean') {
|
|
81
|
+
return { valid: false, error: `${path} must be a boolean` };
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
|
|
85
|
+
case 'date':
|
|
86
|
+
if (typeof value !== 'string') {
|
|
87
|
+
return { valid: false, error: `${path} must be an ISO date string` };
|
|
88
|
+
}
|
|
89
|
+
if (isNaN(Date.parse(value))) {
|
|
90
|
+
return { valid: false, error: `${path} must be a valid ISO date string` };
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
|
|
94
|
+
case 'array':
|
|
95
|
+
if (!Array.isArray(value)) {
|
|
96
|
+
return { valid: false, error: `${path} must be an array` };
|
|
97
|
+
}
|
|
98
|
+
if (rule.minLength && value.length < rule.minLength) {
|
|
99
|
+
return { valid: false, error: `${path} must have at least ${rule.minLength} items` };
|
|
100
|
+
}
|
|
101
|
+
if (rule.maxLength && value.length > rule.maxLength) {
|
|
102
|
+
return { valid: false, error: `${path} must have at most ${rule.maxLength} items` };
|
|
103
|
+
}
|
|
104
|
+
if (rule.items) {
|
|
105
|
+
for (let i = 0; i < value.length; i++) {
|
|
106
|
+
const itemResult = validateValue(value[i], rule.items, `${path}[${i}]`);
|
|
107
|
+
if (!itemResult.valid) return itemResult;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
|
|
112
|
+
case 'object':
|
|
113
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
114
|
+
return { valid: false, error: `${path} must be an object` };
|
|
115
|
+
}
|
|
116
|
+
if (rule.properties) {
|
|
117
|
+
const nestedResult = validate(value, rule.properties);
|
|
118
|
+
if (!nestedResult.valid) {
|
|
119
|
+
return { valid: false, error: `${path}: ${nestedResult.error}` };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
break;
|
|
123
|
+
|
|
124
|
+
case 'enum':
|
|
125
|
+
if (!rule.enumValues?.includes(value as string | number)) {
|
|
126
|
+
return { valid: false, error: `${path} must be one of: ${rule.enumValues?.join(', ')}` };
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return { valid: true };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Pre-built schemas for common tool patterns
|
|
135
|
+
export const ValidationSchemas = {
|
|
136
|
+
stringArray: (minLength = 0): ValidationRule => ({
|
|
137
|
+
type: 'array',
|
|
138
|
+
required: true,
|
|
139
|
+
minLength,
|
|
140
|
+
items: { type: 'string' },
|
|
141
|
+
}),
|
|
142
|
+
|
|
143
|
+
entityArray: (): ValidationRule => ({
|
|
144
|
+
type: 'array',
|
|
145
|
+
required: true,
|
|
146
|
+
items: {
|
|
147
|
+
type: 'object',
|
|
148
|
+
properties: {
|
|
149
|
+
name: { type: 'string', required: true },
|
|
150
|
+
entityType: { type: 'string', required: true },
|
|
151
|
+
observations: { type: 'array', required: true, items: { type: 'string' } },
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
}),
|
|
155
|
+
|
|
156
|
+
relationArray: (): ValidationRule => ({
|
|
157
|
+
type: 'array',
|
|
158
|
+
required: true,
|
|
159
|
+
items: {
|
|
160
|
+
type: 'object',
|
|
161
|
+
properties: {
|
|
162
|
+
from: { type: 'string', required: true },
|
|
163
|
+
to: { type: 'string', required: true },
|
|
164
|
+
relationType: { type: 'string', required: true },
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
}),
|
|
168
|
+
|
|
169
|
+
isoDate: (): ValidationRule => ({
|
|
170
|
+
type: 'date',
|
|
171
|
+
required: true,
|
|
172
|
+
}),
|
|
173
|
+
|
|
174
|
+
nonEmptyString: (): ValidationRule => ({
|
|
175
|
+
type: 'string',
|
|
176
|
+
required: true,
|
|
177
|
+
minLength: 1,
|
|
178
|
+
}),
|
|
179
|
+
};
|
package/src/pde/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDE module — Prompt Decomposition Engine → Structural Tension Chart transformation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { StcMapper, stcMapper } from './stc-mapper.js';
|
|
6
|
+
export { SessionManager, sessionManager } from './session-manager.js';
|
|
7
|
+
export { PDE_MCP_TOOLS } from './mcp-tools.js';
|
|
8
|
+
export { handlePdeTool } from './mcp-handlers.js';
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDE MCP tool dispatch — handles each tool by calling stcMapper + sessionManager
|
|
3
|
+
* Ported from coaia-pde/src/mcp-server.ts handler logic
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { DecompositionResult, McpToolResult } from '../types.js';
|
|
7
|
+
import { stcMapper } from './stc-mapper.js';
|
|
8
|
+
import { sessionManager } from './session-manager.js';
|
|
9
|
+
|
|
10
|
+
export async function handlePdeTool(
|
|
11
|
+
name: string,
|
|
12
|
+
args: Record<string, unknown>
|
|
13
|
+
): Promise<McpToolResult> {
|
|
14
|
+
try {
|
|
15
|
+
switch (name) {
|
|
16
|
+
case 'import_pde_decomposition': {
|
|
17
|
+
const pdeId = args.pde_id as string;
|
|
18
|
+
const workdir = args.workdir as string | undefined;
|
|
19
|
+
|
|
20
|
+
const stored = await sessionManager.loadPdeDecomposition(pdeId, workdir);
|
|
21
|
+
if (!stored) {
|
|
22
|
+
throw new Error(`PDE decomposition ${pdeId} not found in .pde/ directory`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { entities, relations, chartId } = stcMapper.mapDecompositionToChart(
|
|
26
|
+
stored.result,
|
|
27
|
+
stored.prompt,
|
|
28
|
+
{ pdeId: stored.id }
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const session = await sessionManager.initSession(stored.prompt, chartId, stored.id);
|
|
32
|
+
for (const entity of entities) {
|
|
33
|
+
await sessionManager.appendEntity(session.sessionId, entity);
|
|
34
|
+
}
|
|
35
|
+
for (const relation of relations) {
|
|
36
|
+
await sessionManager.appendRelation(session.sessionId, relation);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const desiredOutcome = entities.find(e => e.entityType === 'desired_outcome')?.observations[0];
|
|
40
|
+
const currentReality = entities.find(e => e.entityType === 'current_reality')?.observations;
|
|
41
|
+
const actionSteps = entities
|
|
42
|
+
.filter(e => e.entityType === 'action_step')
|
|
43
|
+
.map(e => ({ name: e.name, description: e.observations[0] }));
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
content: [{
|
|
47
|
+
type: 'text',
|
|
48
|
+
text: JSON.stringify({
|
|
49
|
+
success: true,
|
|
50
|
+
sessionId: session.sessionId,
|
|
51
|
+
chartId,
|
|
52
|
+
pdeDecompositionId: stored.id,
|
|
53
|
+
filePath: await sessionManager.getSessionFilePath(session.sessionId),
|
|
54
|
+
chart: { desiredOutcome, currentReality, actionSteps },
|
|
55
|
+
}, null, 2)
|
|
56
|
+
}]
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
case 'create_stc_from_pde': {
|
|
61
|
+
const resultJson = args.decomposition_result as DecompositionResult;
|
|
62
|
+
const originalPrompt = args.original_prompt as string;
|
|
63
|
+
|
|
64
|
+
const { entities, relations, chartId } = stcMapper.mapDecompositionToChart(
|
|
65
|
+
resultJson,
|
|
66
|
+
originalPrompt
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const session = await sessionManager.initSession(originalPrompt, chartId);
|
|
70
|
+
for (const entity of entities) {
|
|
71
|
+
await sessionManager.appendEntity(session.sessionId, entity);
|
|
72
|
+
}
|
|
73
|
+
for (const relation of relations) {
|
|
74
|
+
await sessionManager.appendRelation(session.sessionId, relation);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const desiredOutcome = entities.find(e => e.entityType === 'desired_outcome')?.observations[0];
|
|
78
|
+
const currentReality = entities.find(e => e.entityType === 'current_reality')?.observations;
|
|
79
|
+
const actionSteps = entities
|
|
80
|
+
.filter(e => e.entityType === 'action_step')
|
|
81
|
+
.map(e => ({ name: e.name, description: e.observations[0] }));
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
content: [{
|
|
85
|
+
type: 'text',
|
|
86
|
+
text: JSON.stringify({
|
|
87
|
+
success: true,
|
|
88
|
+
sessionId: session.sessionId,
|
|
89
|
+
chartId,
|
|
90
|
+
filePath: await sessionManager.getSessionFilePath(session.sessionId),
|
|
91
|
+
chart: { desiredOutcome, currentReality, actionSteps },
|
|
92
|
+
}, null, 2)
|
|
93
|
+
}]
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
case 'list_pde_decompositions': {
|
|
98
|
+
const workdir = args.workdir as string | undefined;
|
|
99
|
+
const decompositions = await sessionManager.listPdeDecompositions(workdir);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
content: [{
|
|
103
|
+
type: 'text',
|
|
104
|
+
text: JSON.stringify({
|
|
105
|
+
count: decompositions.length,
|
|
106
|
+
decompositions: decompositions.map(d => ({
|
|
107
|
+
id: d.id,
|
|
108
|
+
timestamp: d.timestamp,
|
|
109
|
+
prompt: d.prompt.substring(0, 100) + (d.prompt.length > 100 ? '...' : '')
|
|
110
|
+
}))
|
|
111
|
+
}, null, 2)
|
|
112
|
+
}]
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
case 'get_session': {
|
|
117
|
+
const sessionId = args.session_id as string;
|
|
118
|
+
const { session, entities } = await sessionManager.loadSession(sessionId);
|
|
119
|
+
|
|
120
|
+
const chart = entities.find(e => e.entityType === 'structural_tension_chart');
|
|
121
|
+
const desiredOutcome = entities.find(e => e.entityType === 'desired_outcome');
|
|
122
|
+
const currentReality = entities.find(e => e.entityType === 'current_reality');
|
|
123
|
+
const actionSteps = entities.filter(e => e.entityType === 'action_step');
|
|
124
|
+
|
|
125
|
+
const completedCount = actionSteps.filter(a => a.metadata?.completionStatus).length;
|
|
126
|
+
const progress = actionSteps.length > 0 ? completedCount / actionSteps.length : 0;
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
content: [{
|
|
130
|
+
type: 'text',
|
|
131
|
+
text: JSON.stringify({
|
|
132
|
+
session,
|
|
133
|
+
chart: {
|
|
134
|
+
chartId: chart?.metadata?.chartId,
|
|
135
|
+
desiredOutcome: desiredOutcome?.observations[0],
|
|
136
|
+
currentReality: currentReality?.observations,
|
|
137
|
+
dueDate: chart?.metadata?.dueDate,
|
|
138
|
+
progress: Math.round(progress * 100) + '%',
|
|
139
|
+
completedActions: completedCount,
|
|
140
|
+
totalActions: actionSteps.length
|
|
141
|
+
},
|
|
142
|
+
actionSteps: actionSteps.map(a => ({
|
|
143
|
+
name: a.name,
|
|
144
|
+
description: a.observations[0],
|
|
145
|
+
completed: a.metadata?.completionStatus || false,
|
|
146
|
+
dueDate: a.metadata?.dueDate,
|
|
147
|
+
progress: a.observations.slice(1)
|
|
148
|
+
}))
|
|
149
|
+
}, null, 2)
|
|
150
|
+
}]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
case 'list_sessions': {
|
|
155
|
+
const status = args.status as string | undefined;
|
|
156
|
+
let sessions = await sessionManager.listSessions();
|
|
157
|
+
|
|
158
|
+
if (status && status !== 'all') {
|
|
159
|
+
sessions = sessions.filter(s => s.status === status);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
content: [{
|
|
164
|
+
type: 'text',
|
|
165
|
+
text: JSON.stringify({
|
|
166
|
+
count: sessions.length,
|
|
167
|
+
sessions: sessions.map(s => ({
|
|
168
|
+
sessionId: s.sessionId,
|
|
169
|
+
status: s.status,
|
|
170
|
+
createdAt: s.createdAt,
|
|
171
|
+
pdeDecompositionId: s.pdeDecompositionId,
|
|
172
|
+
prompt: s.originalPrompt.substring(0, 100) + (s.originalPrompt.length > 100 ? '...' : '')
|
|
173
|
+
}))
|
|
174
|
+
}, null, 2)
|
|
175
|
+
}]
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
case 'update_action_progress': {
|
|
180
|
+
const sessionId = args.session_id as string;
|
|
181
|
+
const actionName = args.action_name as string;
|
|
182
|
+
const observation = args.observation as string;
|
|
183
|
+
const updateCR = args.update_current_reality as boolean | undefined;
|
|
184
|
+
|
|
185
|
+
const { entities } = await sessionManager.loadSession(sessionId);
|
|
186
|
+
const action = entities.find(e => e.name === actionName);
|
|
187
|
+
|
|
188
|
+
if (!action) {
|
|
189
|
+
throw new Error(`Action step ${actionName} not found`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
action.observations.push(observation);
|
|
193
|
+
await sessionManager.updateEntity(sessionId, actionName, { observations: action.observations });
|
|
194
|
+
|
|
195
|
+
if (updateCR) {
|
|
196
|
+
const chartId = action.metadata?.chartId;
|
|
197
|
+
const realityName = `${chartId}_current_reality`;
|
|
198
|
+
const reality = entities.find(e => e.name === realityName);
|
|
199
|
+
if (reality) {
|
|
200
|
+
reality.observations.push(`Progress: ${observation}`);
|
|
201
|
+
await sessionManager.updateEntity(sessionId, realityName, { observations: reality.observations });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
content: [{
|
|
207
|
+
type: 'text',
|
|
208
|
+
text: JSON.stringify({ success: true, actionName, observation })
|
|
209
|
+
}]
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
case 'mark_action_complete': {
|
|
214
|
+
const sessionId = args.session_id as string;
|
|
215
|
+
const actionName = args.action_name as string;
|
|
216
|
+
const completionNote = args.completion_note as string | undefined;
|
|
217
|
+
|
|
218
|
+
const { entities } = await sessionManager.loadSession(sessionId);
|
|
219
|
+
const action = entities.find(e => e.name === actionName);
|
|
220
|
+
|
|
221
|
+
if (!action) {
|
|
222
|
+
throw new Error(`Action step ${actionName} not found`);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!action.metadata) action.metadata = {};
|
|
226
|
+
action.metadata.completionStatus = true;
|
|
227
|
+
action.metadata.updatedAt = new Date().toISOString();
|
|
228
|
+
|
|
229
|
+
if (completionNote) {
|
|
230
|
+
action.observations.push(`Completed: ${completionNote}`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
await sessionManager.updateEntity(sessionId, actionName, {
|
|
234
|
+
observations: action.observations,
|
|
235
|
+
metadata: action.metadata
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const chartId = action.metadata?.chartId;
|
|
239
|
+
const realityName = `${chartId}_current_reality`;
|
|
240
|
+
const reality = entities.find(e => e.name === realityName);
|
|
241
|
+
if (reality) {
|
|
242
|
+
reality.observations.push(`Completed: ${action.observations[0]}`);
|
|
243
|
+
await sessionManager.updateEntity(sessionId, realityName, { observations: reality.observations });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
content: [{
|
|
248
|
+
type: 'text',
|
|
249
|
+
text: JSON.stringify({ success: true, actionName, completed: true })
|
|
250
|
+
}]
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
case 'add_action_step': {
|
|
255
|
+
const sessionId = args.session_id as string;
|
|
256
|
+
const description = args.action_description as string;
|
|
257
|
+
const currentReality = args.current_reality as string | undefined;
|
|
258
|
+
|
|
259
|
+
const { session, entities } = await sessionManager.loadSession(sessionId);
|
|
260
|
+
const chart = entities.find(e => e.entityType === 'structural_tension_chart');
|
|
261
|
+
const chartId = (chart?.metadata?.chartId as string) || session.masterChartId;
|
|
262
|
+
|
|
263
|
+
const existingActions = entities.filter(e => e.entityType === 'action_step');
|
|
264
|
+
const actionNum = existingActions.length + 1;
|
|
265
|
+
const actionName = `${chartId}_action_${actionNum}`;
|
|
266
|
+
const now = new Date().toISOString();
|
|
267
|
+
|
|
268
|
+
const newAction = {
|
|
269
|
+
name: actionName,
|
|
270
|
+
entityType: 'action_step',
|
|
271
|
+
observations: currentReality ? [description, `Current: ${currentReality}`] : [description],
|
|
272
|
+
metadata: {
|
|
273
|
+
chartId,
|
|
274
|
+
completionStatus: false,
|
|
275
|
+
level: 1,
|
|
276
|
+
parentChart: chartId,
|
|
277
|
+
createdAt: now,
|
|
278
|
+
updatedAt: now,
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
await sessionManager.appendEntity(sessionId, newAction);
|
|
283
|
+
await sessionManager.appendRelation(sessionId, {
|
|
284
|
+
from: `${chartId}_chart`,
|
|
285
|
+
to: actionName,
|
|
286
|
+
relationType: 'has_action_step',
|
|
287
|
+
metadata: { createdAt: now }
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
return {
|
|
291
|
+
content: [{
|
|
292
|
+
type: 'text',
|
|
293
|
+
text: JSON.stringify({ success: true, actionName, description })
|
|
294
|
+
}]
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
case 'update_current_reality': {
|
|
299
|
+
const sessionId = args.session_id as string;
|
|
300
|
+
const observations = args.observations as string[];
|
|
301
|
+
|
|
302
|
+
const { session, entities } = await sessionManager.loadSession(sessionId);
|
|
303
|
+
const chartId = session.masterChartId;
|
|
304
|
+
const realityName = `${chartId}_current_reality`;
|
|
305
|
+
const reality = entities.find(e => e.name === realityName);
|
|
306
|
+
|
|
307
|
+
if (!reality) {
|
|
308
|
+
throw new Error(`Current reality not found for chart ${chartId}`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
reality.observations.push(...observations);
|
|
312
|
+
await sessionManager.updateEntity(sessionId, realityName, { observations: reality.observations });
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
content: [{
|
|
316
|
+
type: 'text',
|
|
317
|
+
text: JSON.stringify({ success: true, added: observations.length, totalObservations: reality.observations.length })
|
|
318
|
+
}]
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
case 'complete_session': {
|
|
323
|
+
const sessionId = args.session_id as string;
|
|
324
|
+
const finalNote = args.final_note as string | undefined;
|
|
325
|
+
|
|
326
|
+
await sessionManager.updateSession(sessionId, { status: 'completed' });
|
|
327
|
+
|
|
328
|
+
if (finalNote) {
|
|
329
|
+
const { session, entities } = await sessionManager.loadSession(sessionId);
|
|
330
|
+
const realityName = `${session.masterChartId}_current_reality`;
|
|
331
|
+
const reality = entities.find(e => e.name === realityName);
|
|
332
|
+
if (reality) {
|
|
333
|
+
reality.observations.push(`Final: ${finalNote}`);
|
|
334
|
+
await sessionManager.updateEntity(sessionId, realityName, { observations: reality.observations });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
content: [{
|
|
340
|
+
type: 'text',
|
|
341
|
+
text: JSON.stringify({ success: true, sessionId, status: 'completed' })
|
|
342
|
+
}]
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
default:
|
|
347
|
+
throw new Error(`Unknown PDE tool: ${name}`);
|
|
348
|
+
}
|
|
349
|
+
} catch (error: unknown) {
|
|
350
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
351
|
+
return {
|
|
352
|
+
content: [{
|
|
353
|
+
type: 'text',
|
|
354
|
+
text: `Error: ${message}`,
|
|
355
|
+
}],
|
|
356
|
+
isError: true
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
}
|