@appaflytech/zeyrek-ai-mcp 0.0.47 → 0.0.49
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/.env.example +5 -0
- package/dist/config.js +2 -0
- package/dist/knowledge/access.js +4 -3
- package/dist/knowledge/assistantRun.js +165 -0
- package/dist/knowledge/componentCatalog.js +31 -0
- package/dist/knowledge/componentEvents.js +3 -0
- package/dist/knowledge/mobileComponentCatalog.js +15 -0
- package/dist/knowledge/motionResolve.js +1 -1
- package/dist/knowledge/styleResolve.js +7 -0
- package/dist/platform/errors.js +9 -0
- package/dist/platform/http.js +7 -2
- package/dist/platform/tokenVault.js +15 -0
- package/dist/resources/guides/assistant.md +194 -0
- package/dist/resources/guides/deployment.md +19 -1
- package/dist/resources/schema-fingerprint.json +4 -4
- package/dist/resources/schemas/AccessMatrixDto.json +2 -1
- package/dist/resources/schemas/AccessPermissionDto.json +2 -1
- package/dist/resources/schemas/AccessResourceDto.json +2 -1
- package/dist/resources/schemas/AssistantCapabilitiesDto.json +256 -0
- package/dist/resources/schemas/AssistantPublishStateDto.json +57 -0
- package/dist/resources/schemas/AssistantScenarioDto.json +91 -0
- package/dist/resources/schemas/AssistantScenarioRunDto.json +62 -0
- package/dist/resources/schemas/AssistantSettingsDto.json +52 -0
- package/dist/resources/schemas/AssistantSkillDto.json +78 -0
- package/dist/resources/schemas/AssistantSummaryDto.json +100 -0
- package/dist/resources/schemas/CreateAssistantScenarioCommand.json +81 -0
- package/dist/resources/schemas/CreateAssistantSkillCommand.json +83 -0
- package/dist/resources/schemas/CreateMicroserviceCommand.json +16 -0
- package/dist/resources/schemas/DescribeAssistantCapabilityCommand.json +145 -0
- package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +5 -1
- package/dist/resources/schemas/MicroserviceDto.json +16 -0
- package/dist/resources/schemas/PublishAssistantCommand.json +15 -0
- package/dist/resources/schemas/RunAssistantScenariosCommand.json +39 -0
- package/dist/resources/schemas/SimulateAssistantCommand.json +106 -0
- package/dist/resources/schemas/UpdateAssistantScenarioCommand.json +79 -0
- package/dist/resources/schemas/UpdateAssistantSettingsCommand.json +52 -0
- package/dist/resources/schemas/UpdateAssistantSkillCommand.json +81 -0
- package/dist/resources/schemas/UpdateMicroserviceCommand.json +16 -0
- package/dist/server.js +2 -0
- package/dist/tools/applications.js +4 -4
- package/dist/tools/assistant.js +382 -0
- package/dist/tools/deployments.js +9 -3
- package/dist/tools/microservices.js +1 -1
- package/dist/tools/mobile.js +2 -2
- package/dist/tools/projects.js +2 -2
- package/dist/tools/templates.js +1 -1
- package/dist/tools/themes.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive } from './common.js';
|
|
2
|
+
import { parseSseEvents, summarizeAssistantRun, summarizeCapabilityPage, mergeCapabilityTexts } from '../knowledge/assistantRun.js';
|
|
3
|
+
import { PolicyBlockError } from '../knowledge/policy.js';
|
|
4
|
+
import { resolveActorFields } from '../platform/tokenVault.js';
|
|
5
|
+
import { handle } from '../platform/responses.js';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
const authorizationSourceParam = z.enum(['platform', 'application-middleware'])
|
|
8
|
+
.describe("Yetki kaynağı: 'platform' kompleks + enforce projede platformun erişim kararları; 'application-middleware' simple projede uygulamanın kendi kimlik ucu (identityMicroserviceId zorunlu)");
|
|
9
|
+
const textMapParam = z.record(z.string()).describe("Dil koduna göre metin: { tr: '…', en: '…' }");
|
|
10
|
+
const historyParam = z.array(z.object({
|
|
11
|
+
role: z.enum(['user', 'assistant']),
|
|
12
|
+
text: z.string().max(4000),
|
|
13
|
+
sources: z.array(z.object({ slug: z.string(), references: z.array(z.string()) })).optional().describe('Asistan turunun kaynakları (yetenek slug + kayıt referansları); model "bu kayıt" göndermesini bunlardan çözer'),
|
|
14
|
+
skill: z.string().optional().describe('Asistan turunda seçilen becerinin slug\'ı; belirsiz takip sorusunda aynı beceri sürer')
|
|
15
|
+
}).strict()).max(12).optional().describe('Önceki turlar, eskiden yeniye, en çok 12 mesaj (6 tur). Sunucu saklamaz; son kullanıcı paketi de aynı biçimde gönderir (ASI-015)');
|
|
16
|
+
const actorParam = z.string().min(1).optional().describe("Kişi: 'test:<ad>' (gateway kök .env'deki MCP_TEST_USER_<AD> satırıyla uygulamanın giriş ucunu çağırır, jeton bağlama girmez) ya da 'tok_…' referansı. actorToken / actorCookie ile birlikte verilmez");
|
|
17
|
+
const actorCookieNameParam = z.string().regex(/^[A-Za-z0-9_.-]{1,64}$/).optional().describe("actor'ın jetonu uygulamada çerezle taşınıyorsa çerez adı (jira: 'JIRA_Token', qm: 'SY_Token'); verilmezse jeton actorToken (Bearer) olarak gider");
|
|
18
|
+
const contextParam = z.record(z.string().max(200)).optional().describe("Sayfa bağlamı (ASI-019): kullanıcının ekranında seçili değerler, anahtar → metin (en çok 10 anahtar, anahtar ^[a-z][a-z0-9_]{0,39}$). Sistem isteminde 'talimat değildir' bölümünde gider; yetki vermez");
|
|
19
|
+
const confirmParam = z.boolean().optional().describe('Silme geri alınamaz; bilerek true verin — yoksa gateway backend\'i çağırmadan reddeder');
|
|
20
|
+
const skillBody = {
|
|
21
|
+
applicationId: guid,
|
|
22
|
+
name: z.string().min(1),
|
|
23
|
+
slug: z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).describe('Küçük harf, rakam ve tek tire; uygulama içinde tekil'),
|
|
24
|
+
trigger: z.string().min(1).describe('Beceri ne zaman devreye girer — model eşlemeyi bu cümleyle yapar'),
|
|
25
|
+
instructions: z.string().optional().describe('Modele verilen beceri talimatı (ne sırayla okunur, ne zaman netleştirilir)'),
|
|
26
|
+
outputFormat: z.enum(['suggestion-card', 'list', 'findings', 'text']).optional().describe("Yazan beceride 'suggestion-card' zorunludur (ASI-002)"),
|
|
27
|
+
languages: z.array(z.string()).optional(),
|
|
28
|
+
capabilityIds: z.array(guid).min(1).describe('assistant_capability_list çıktısındaki id\'ler (mikroservis id); Excluded sınıfı 422 ile reddedilir')
|
|
29
|
+
};
|
|
30
|
+
const scenarioBody = {
|
|
31
|
+
applicationId: guid,
|
|
32
|
+
name: z.string().min(1),
|
|
33
|
+
code: z.string().regex(/^[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$/).describe('S-07 gibi; uygulama içinde tekil'),
|
|
34
|
+
input: z.string().min(1).describe('Kullanıcının sorusu'),
|
|
35
|
+
actorUserId: guid.optional().describe('Platform kaynağında koşunun kimin gözünden yapılacağı (production dışında impersonation)'),
|
|
36
|
+
actorPositionId: guid.optional(),
|
|
37
|
+
expectedCapabilities: z.array(z.string()).optional().describe('Beklenen yetenek dizisi, birebir sıra; onay kartı yeteneği de sayılır'),
|
|
38
|
+
expectedFields: z.array(z.string()).optional().describe('Öneri kartında bulunması gereken alanlar'),
|
|
39
|
+
expectedSource: z.string().optional().describe('Cevabın dayandığı kaynak yeteneğin slug\'ı'),
|
|
40
|
+
expectedRefusal: z.enum(['person-judgement', 'prediction', 'no-evidence']).optional().describe('Beklenen ret sınıfı; verilirse cevapta refusal olayı aranır'),
|
|
41
|
+
isLeakProbe: z.boolean().optional().describe('true: hiçbir araç sonucu kayıt döndürmemeli (yetki sızıntısı provası)')
|
|
42
|
+
};
|
|
43
|
+
const CAPABILITY_PAGE_LIMIT = 100;
|
|
44
|
+
const CAPABILITY_DRAFT_LIMIT = 20;
|
|
45
|
+
const CAPABILITY_DRAFT_CHUNK = 5;
|
|
46
|
+
const CAPABILITY_DRAFT_SCAN = 100;
|
|
47
|
+
const parameterSourceParam = z.object({
|
|
48
|
+
capability: z.string().min(1).describe("Kaynak okuma yeteneği: slug (gateway id'ye çevirir) ya da id"),
|
|
49
|
+
valueField: z.string().min(1).max(64).describe("Seçeneğin değeri olan sonuç alanı, ör. 'id'"),
|
|
50
|
+
labelField: z.string().min(1).max(64).describe("Seçeneğin görünen adı olan sonuç alanı, ör. 'name'"),
|
|
51
|
+
searchParameter: z.string().min(1).max(64).optional().describe('Kaynağın arama metni parametresi; yoksa kart ilk 50 seçenek içinde yerel arar'),
|
|
52
|
+
arguments: z.record(z.unknown()).optional().describe("Kaynağa her çağrıda giden argümanlar, ör. { status: 'Active' }. '{alan}' biçimindeki değer kartın o alanının değeriyle doldurulur (ör. { company_id: '{company_id}' } — firmanın izin türleri); alan boşsa kaynak çağrılmaz, alan seçilince kart yeniden önerilir ve seçenekler gelir"),
|
|
53
|
+
listField: z.string().min(1).max(64).regex(/^[^.]+$/).optional().describe("Seçeneklerin durduğu dizi (kaynağın kendi listField'ından önce gelir), ör. kimlik ucunun 'companies' dizisi; nokta içermez")
|
|
54
|
+
});
|
|
55
|
+
export function registerAssistantTools(server, platform) {
|
|
56
|
+
const config = platform.config;
|
|
57
|
+
const listSkills = async (project, environment, applicationId) => {
|
|
58
|
+
const payload = await platform.tenant('GET', project, '/assistant/skills', { environment, query: { applicationId } });
|
|
59
|
+
return payload?.data ?? [];
|
|
60
|
+
};
|
|
61
|
+
const listScenarios = async (project, environment, applicationId) => {
|
|
62
|
+
const payload = await platform.tenant('GET', project, '/assistant/scenarios', { environment, query: { applicationId } });
|
|
63
|
+
return payload?.data ?? [];
|
|
64
|
+
};
|
|
65
|
+
const guardProduction = (tool, environment) => {
|
|
66
|
+
if ((environment ?? config.defaultEnvironment) === 'production') {
|
|
67
|
+
throw new PolicyBlockError([{
|
|
68
|
+
ruleId: 'ASI-013',
|
|
69
|
+
severity: 'block',
|
|
70
|
+
message: `${tool}: asistan tanımı production'da yazılmaz. development'ta kurun, senaryoları koşun ve yayınlayın; production'a taşıma insan onaylı deployment işidir.`,
|
|
71
|
+
guide: 'ngapps://guides/assistant'
|
|
72
|
+
}], false);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const streamEvents = async (project, environment, path, body) => {
|
|
76
|
+
const payload = await platform.tenant('POST', project, path, { environment, body, expectBinary: true });
|
|
77
|
+
return parseSseEvents(Buffer.from(payload?.bytes ?? []).toString('utf8'));
|
|
78
|
+
};
|
|
79
|
+
server.registerTool('assistant_settings_get', {
|
|
80
|
+
title: 'Asistan ayarını getir',
|
|
81
|
+
description: 'Uygulamanın asistan ayarını getirir (GET /applications/{applicationId}/assistant): açık mı, yetki kaynağı, kimlik ucu, izin listesi ucu. Ayar hiç yazılmamışsa enabled:false ile varsayılan döner.',
|
|
82
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid },
|
|
83
|
+
annotations: readOnly
|
|
84
|
+
}, handle(config, ({ project, environment, applicationId }) => platform.tenant('GET', project, `/applications/${applicationId}/assistant`, { environment })));
|
|
85
|
+
server.registerTool('assistant_settings_update', {
|
|
86
|
+
title: 'Asistan ayarını yaz',
|
|
87
|
+
description: "Uygulamanın asistanını açar/kapatır ve yetki kaynağını seçer (PUT /applications/{applicationId}/assistant). Gövde = UpdateAssistantSettingsCommand. 'application-middleware' kaynağında identityMicroserviceId zorunludur; gateway bunu backend'e gitmeden reddeder (ASI-001). Kapı kapalıysa backend 422 AssistantGateClosed döner ve nedeni mesajdadır: access-model (platform kaynağı kompleks proje ister), enforcement-mode (ortam enforce değil), identity-microservice, no-capabilities (kimlik middleware'inin koruduğu uç yok). Açılış 'assistant' sistem modülünü de kurar: üç başlangıç becerisi + altı senaryo; Süreç Yönetimi modülü yoksa süreç becerileri atlanır (ASI-003).",
|
|
88
|
+
inputSchema: {
|
|
89
|
+
project: projectParam,
|
|
90
|
+
environment: environmentParam,
|
|
91
|
+
applicationId: guid,
|
|
92
|
+
enabled: z.boolean(),
|
|
93
|
+
authorizationSource: authorizationSourceParam,
|
|
94
|
+
identityMicroserviceId: guid.optional().describe("Kullanıcıyı doğrulayan uç; 'application-middleware' kaynağında zorunlu"),
|
|
95
|
+
permissionListMicroserviceId: guid.optional().describe("Oturumdaki kullanıcının izinlerini dönen uç (application-middleware; backend ADR 0069): her sohbet turunun başında kullanıcının oturumuyla bir kez çağrılır"),
|
|
96
|
+
permissionKeyField: z.string().optional().describe("İzin listesinin yolu: '.' alan, '*' bütün öğeler/değerler, '|' seçenekler; yolun sonu true ise bütün izinler, nesneyse anahtarları, diziyse metinleri. ör. 'authz.is_admin|authz.companies.*.grants'"),
|
|
97
|
+
guide: z.record(z.string().max(12000)).optional().describe("Uygulama rehberi, dil başına markdown (≤ 12000 karakter, projenin dilleri). Backend BİRLEŞTİRİR: verilmeyen dil korunur, bir dile '' vermek o dili siler. Asistan sistem isteminde '## Uygulama rehberi' olarak taşınır; yayınla son kullanıcıya geçer (sağlama toplamına girer). Taslak için assistant_guide_draft")
|
|
98
|
+
},
|
|
99
|
+
annotations: updates
|
|
100
|
+
}, handle(config, ({ project, environment, applicationId, ...body }) => {
|
|
101
|
+
guardProduction('assistant_settings_update', environment);
|
|
102
|
+
if (body.authorizationSource === 'application-middleware' && !body.identityMicroserviceId) {
|
|
103
|
+
throw new Error("assistant_settings_update: authorizationSource 'application-middleware' iken identityMicroserviceId gerekir — asistan kullanıcıyı bu uçla tanır (reason: identity-microservice). assistant_capability_list ile Authenticate middleware'li ucu bulup verin.");
|
|
104
|
+
}
|
|
105
|
+
return platform.tenant('PUT', project, `/applications/${applicationId}/assistant`, { environment, body });
|
|
106
|
+
}));
|
|
107
|
+
server.registerTool('assistant_guide_draft', {
|
|
108
|
+
title: 'Uygulama rehberini taslakla',
|
|
109
|
+
description: "Asistanın modeliyle uygulama rehberi TASLAĞI üretir (POST /assistant/guide/draft): menü (layout'taki navigations öğeleri), beceri adları/tetikleyicileri ve kullanılan uçların açıklamalarının ilk cümlesinden dil başına markdown (Uygulama ne işe yarar · Menü · Süreçler · Sık sorulanlar). KAYDETMEZ — süreç adımlarını uygulamanın kendi dokümanıyla düzeltip assistant_settings_update { guide } ile yazın. Model maliyeti doğurur; assistant.write izni.",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
project: projectParam,
|
|
112
|
+
environment: environmentParam,
|
|
113
|
+
applicationId: guid,
|
|
114
|
+
languages: z.array(z.string().min(2)).min(1).max(6).describe("Projenin dilleri, ör. ['tr','en']")
|
|
115
|
+
},
|
|
116
|
+
annotations: readOnly
|
|
117
|
+
}, handle(config, async ({ project, environment, applicationId, languages }) => {
|
|
118
|
+
const payload = await platform.tenant('POST', project, '/assistant/guide/draft', { environment, body: { applicationId, languages } });
|
|
119
|
+
return { guides: payload?.data?.guides ?? {}, next: 'Gözden geçirin; süreç adımlarını uygulama dokümanıyla düzeltin, sonra assistant_settings_update { guide } ile yazın ve yayınlayın.' };
|
|
120
|
+
}));
|
|
121
|
+
server.registerTool('assistant_status', {
|
|
122
|
+
title: 'Asistan durumunu özetle',
|
|
123
|
+
description: 'Uygulamanın asistan hazırlık özetini tek ucuz çağrıda verir (GET /assistant/summary?applicationId=): açık mı, yetki kaynağı, yetenek ve açıklamasız yetenek sayısı, modül/önek grupları (key, moduleName, count, undescribedCount — key assistant_capability_list groupKey\'ine gider), beceri/senaryo/geçen senaryo sayısı, kalan sızıntı provası, son yayın zamanı. Kataloğu kurmaz; işe buradan başlayın, ayrıntı için assistant_capability_list.',
|
|
124
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid },
|
|
125
|
+
annotations: readOnly
|
|
126
|
+
}, handle(config, ({ project, environment, applicationId }) => platform.tenant('GET', project, '/assistant/summary', { environment, query: { applicationId } })));
|
|
127
|
+
server.registerTool('assistant_usage', {
|
|
128
|
+
title: 'Asistan kullanımını raporla',
|
|
129
|
+
description: "Uygulamanın asistan kullanım panosunu verir (GET /assistant/usage): toplamlar (konuşma, tekil kullanıcı, token giriş/çıkış/önbellek, tahmini maliyet — fiyatı tanımlı olmayan modelde cost null, ortalama süre, araç çağrısı, 👍/👎), güne göre seri, beceriye ve sonuca göre dağılım (answered · suggested · refused · denied · clarify · error · partial — araç sınırına gelip okuduğuyla cevap verdi), en çok erişim reddi alan yetenekler ve son geri bildirimler. from/to gün (YYYY-AA-GG, ikisi de dahil; varsayılan son 30 gün, en çok 92 gün). Nerede yetersiz kaldığını görmek için outcomes ve deniedCapabilities'e bakın. assistant.read izni.",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
project: projectParam,
|
|
132
|
+
environment: environmentParam,
|
|
133
|
+
applicationId: guid,
|
|
134
|
+
from: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe('İlk gün, ör. 2026-10-01'),
|
|
135
|
+
to: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe('Son gün (dahil), ör. 2026-10-22')
|
|
136
|
+
},
|
|
137
|
+
annotations: readOnly
|
|
138
|
+
}, handle(config, ({ project, environment, applicationId, from, to }) => platform.tenant('GET', project, '/assistant/usage', { environment, query: { applicationId, from, to } })));
|
|
139
|
+
server.registerTool('assistant_capability_list', {
|
|
140
|
+
title: 'Yetenek kataloğunu listele',
|
|
141
|
+
description: `Uygulamanın asistan yetenek kataloğundan bir sayfa getirir (GET /assistant/capabilities/page): her mikroservis bir yetenektir; kind 'read' / 'write' / 'excluded' (exclusion nedeniyle), origin 'own' / 'module', hasDescription + descriptionLanguages (açıklaması yazılmış diller; eksikse assistant_capability_describe). Sunucu süzer: searchTerm (slug/ad), groupKey (assistant_status gruplarından), ids, undescribed:true; total bu süzgecin toplamıdır. kind / origin / includeExcluded:false yalnız dönen sayfanın satırlarını süzer (hiddenOnPage gizlenen sayısı) — tam liste için groupKey/searchTerm ile daraltın. undescribed:true ile açıkladıkça liste kısalır: hep pageIndex 0 isteyin. detail:true şemayı, dil başına metinleri, ipuçlarını da verir. pageLength en çok ${CAPABILITY_PAGE_LIMIT} (varsayılan 50).`,
|
|
142
|
+
inputSchema: {
|
|
143
|
+
project: projectParam,
|
|
144
|
+
environment: environmentParam,
|
|
145
|
+
applicationId: guid,
|
|
146
|
+
searchTerm: z.string().optional().describe('Slug ya da adda geçen metin'),
|
|
147
|
+
groupKey: z.string().optional().describe("assistant_status gruplarındaki key: 'module:<templateId>', 'prefix:<önek>' ya da 'other'"),
|
|
148
|
+
ids: z.array(guid).optional().describe('Yalnız bu yetenekler (mikroservis id)'),
|
|
149
|
+
undescribed: z.boolean().optional().describe('true: yalnız asistan açıklaması olmayanlar (sunucuda süzülür)'),
|
|
150
|
+
kind: z.enum(['read', 'write', 'excluded']).optional().describe('Yalnız dönen sayfada süzülür'),
|
|
151
|
+
origin: z.enum(['own', 'module']).optional().describe('Yalnız dönen sayfada süzülür'),
|
|
152
|
+
includeExcluded: z.boolean().optional().describe('Varsayılan true; false dönen sayfadaki katalog dışı satırları gizler'),
|
|
153
|
+
detail: z.boolean().optional().describe('true: inputSchema/outputSchema, dil başına açıklama/uyarı ve ipuçları da döner'),
|
|
154
|
+
pageIndex: pagingParams.pageIndex,
|
|
155
|
+
pageLength: z.number().int().min(1).max(CAPABILITY_PAGE_LIMIT).optional().describe(`Sayfa boyu, varsayılan 50, en çok ${CAPABILITY_PAGE_LIMIT}`)
|
|
156
|
+
},
|
|
157
|
+
annotations: readOnly
|
|
158
|
+
}, handle(config, async ({ project, environment, applicationId, searchTerm, groupKey, ids, undescribed, kind, origin, includeExcluded, detail, pageIndex, pageLength }) => {
|
|
159
|
+
const length = pageLength ?? 50;
|
|
160
|
+
const index = pageIndex ?? 0;
|
|
161
|
+
const payload = await platform.tenant('GET', project, '/assistant/capabilities/page', { environment, query: { applicationId, searchTerm, groupKey, ids, onlyUndescribed: undescribed, pageIndex: index, pageLength: length } });
|
|
162
|
+
return summarizeCapabilityPage({ ...payload, pageIndex: index, pageLength: length }, { kind, origin, includeExcluded, detail });
|
|
163
|
+
}));
|
|
164
|
+
server.registerTool('assistant_capability_draft', {
|
|
165
|
+
title: 'Yetenek açıklaması taslakla',
|
|
166
|
+
description: `Asistanın modeliyle yetenek açıklaması TASLAĞI üretir (POST /assistant/capabilities/draft): uç adı + giriş/çıkış şeması + iş akışı adımları ve kodu okunur, istenen dillerde açıklama, uyarı ve ipucu (zorunlu parametre, koddan bulunan değer listesi) önerilir. KAYDETMEZ — gözden geçirip assistant_capability_describe ile yazın (insan onayı). ids (en çok ${CAPABILITY_DRAFT_LIMIT}) ya da groupKey (assistant_status gruplarından; o grubun açıklamasız ilk ${CAPABILITY_DRAFT_LIMIT} yeteneği). Model maliyeti doğurur; katalog dışı (excluded) yetenek taslaklanmaz. assistant.write izni.`,
|
|
167
|
+
inputSchema: {
|
|
168
|
+
project: projectParam,
|
|
169
|
+
environment: environmentParam,
|
|
170
|
+
applicationId: guid,
|
|
171
|
+
ids: z.array(guid).max(CAPABILITY_DRAFT_LIMIT).optional().describe('Taslaklanacak yetenekler (mikroservis id)'),
|
|
172
|
+
groupKey: z.string().optional().describe('ids yerine: bu grubun açıklamasız yetenekleri'),
|
|
173
|
+
languages: z.array(z.string().min(2)).min(1).max(6).describe("Projenin dilleri, ör. ['tr','en']")
|
|
174
|
+
},
|
|
175
|
+
annotations: readOnly
|
|
176
|
+
}, handle(config, async ({ project, environment, applicationId, ids, groupKey, languages }) => {
|
|
177
|
+
let capabilityIds = ids ?? [];
|
|
178
|
+
if (capabilityIds.length === 0) {
|
|
179
|
+
if (!groupKey)
|
|
180
|
+
throw new Error('assistant_capability_draft: ids ya da groupKey verin — groupKey için assistant_status gruplarına bakın.');
|
|
181
|
+
const page = await platform.tenant('GET', project, '/assistant/capabilities/page', { environment, query: { applicationId, groupKey, onlyUndescribed: true, pageLength: CAPABILITY_DRAFT_SCAN } });
|
|
182
|
+
capabilityIds = (page?.data ?? []).filter(row => String(row.kind ?? '').toLowerCase() !== 'excluded').slice(0, CAPABILITY_DRAFT_LIMIT).map(row => row.id);
|
|
183
|
+
}
|
|
184
|
+
if (capabilityIds.length === 0)
|
|
185
|
+
return { drafts: [], note: 'Bu grupta açıklamasız ve katalogdaki yetenek kalmadı.' };
|
|
186
|
+
const chunks = Array.from({ length: Math.ceil(capabilityIds.length / CAPABILITY_DRAFT_CHUNK) }, (_, index) => capabilityIds.slice(index * CAPABILITY_DRAFT_CHUNK, (index + 1) * CAPABILITY_DRAFT_CHUNK));
|
|
187
|
+
const answers = await Promise.all(chunks.map(chunk => platform.tenant('POST', project, '/assistant/capabilities/draft', { environment, body: { applicationId, ids: chunk, languages } })));
|
|
188
|
+
return { drafts: answers.flatMap(answer => answer?.data ?? []), next: 'Gözden geçirin; uygunları assistant_capability_describe ile yazın (descriptions → assistantDescription, cautions → assistantCaution, hints → hints).' };
|
|
189
|
+
}));
|
|
190
|
+
server.registerTool('assistant_capability_describe', {
|
|
191
|
+
title: 'Yeteneği asistan için açıkla',
|
|
192
|
+
description: "Bir yeteneğin son kullanıcı diline çevrilmiş açıklamasını, uyarısını, kayıt sayfasını ve ipuçlarını yazar (PUT /assistant/capabilities/{microserviceId}). Gateway önce mevcut hâli okur ve BİRLEŞTİRİR: verilmeyen alan korunur, null o alanı temizler, açıklama/uyarı dil dil birleşir (yalnız en'i yazmak tr'yi silmez; bir dile '' vermek o dili siler). Açıklama beş şeyi söyler: ne yapar, ne yapmaz, tuzak, gerekli parametreler, ne zaman kullanılmaz. Geliştirici description alanına dokunmaz. Yayın kapısı bir beceride kullanılan açıklamasız yeteneği kapatır (ASI-004). Production'da yazmaz (ASI-013).",
|
|
193
|
+
inputSchema: {
|
|
194
|
+
project: projectParam,
|
|
195
|
+
environment: environmentParam,
|
|
196
|
+
applicationId: guid.describe('Yeteneğin uygulaması — mevcut hâl bu uygulamanın kataloğundan okunur'),
|
|
197
|
+
microserviceId: guid.describe('assistant_capability_list çıktısındaki id'),
|
|
198
|
+
assistantDescription: textMapParam.optional().describe("Dil koduna göre açıklama, mevcut dillerle birleşir: { en: '…' }"),
|
|
199
|
+
assistantCaution: textMapParam.nullable().optional().describe('Dil koduna göre uyarı, birleşir; null bütün uyarıyı temizler'),
|
|
200
|
+
recordPageId: guid.nullable().optional().describe('Kayıt sayfası (ASI-011): liste/tekil sonuçtaki her satır için "Aç" bağlantısı bu sayfaya gider; null bağlantıyı kaldırır'),
|
|
201
|
+
recordQuery: z.string().nullable().optional().describe("Kayıt sayfası sorgu şablonu, varsayılan 'id={id}'; {id} zorunlu"),
|
|
202
|
+
formPageId: guid.nullable().optional().describe('Form sayfası (yalnız write yetenek): yazma kartında "Formda aç" bağlantısı bu sayfaya gider, bilinen alanlar formQuery ile taşınır; null bağlantıyı kaldırır'),
|
|
203
|
+
formQuery: z.string().max(256).nullable().optional().describe("Form sayfası sorgu şablonu, yalnız ad={parametre} çiftleri & ile (en çok 10), ör. 'definition={definition}&company={company}'; değeri olmayan ya da gizli alan bağlantıya yazılmaz"),
|
|
204
|
+
hints: z.object({
|
|
205
|
+
requiredParameters: z.array(z.string()).optional(),
|
|
206
|
+
parameterValues: z.record(z.array(z.string())).optional(),
|
|
207
|
+
listField: z.string().max(64).optional().describe("Sonuçta satırların durduğu dizi alanı (ASI-017), ör. 'items' — zarf { total, items } ya da birden çok dizi taşıyan yanıtta kayıt bloğu ve sayım bu diziden okunur; nokta içermez"),
|
|
208
|
+
parameterSources: z.record(parameterSourceParam).optional().describe("Parametre → değerlerini listeleyen okuma yeteneği (ASI-020, en çok 10): kartta eksik kalan alan 'Hangi …?' diye bu yeteneğin gerçek seçenekleriyle sorulur, seçilen kimliğin adı kartta görünür. Kaynak aynı uygulamanın okuma yeteneğidir; gateway slug'ı id'ye çevirir, parametrenin şemada olduğunu ve kaynağın 'read' olduğunu yazmadan denetler"),
|
|
209
|
+
parameterLabels: z.record(z.record(z.string().max(60))).optional().describe("Dil → parametre → kartta görünen etiket, ör. { tr: { company: 'Firma' }, en: { company: 'Company' } } (dil başına en çok 40 parametre)"),
|
|
210
|
+
permissionKey: z.string().regex(/^[A-Za-z0-9_.:-]{1,100}(\|[A-Za-z0-9_.:-]{1,100}){0,9}$/).optional().describe("Bu yeteneği kullanmak için gereken izin anahtarı (backend ADR 0069), ör. 'leave.create'; '|' ile en çok 10 seçenek ('leave.create|leave.manage' = herhangi biri yeter). Ayarda izin listesi ucu (permissionListMicroserviceId + permissionKeyField) varsa anahtarı kullanıcının listesinde olmayan yetenek o turda araçlardan düşer ve asistan 'yetkin yok' der; anahtarsız yetenek süzülmez")
|
|
211
|
+
}).nullable().optional().describe("Araç şemasına ipucu (ASI-012), verilirse bütünüyle yer değiştirir: zorunlu parametreler, değer listeleri, liste alanı, seçenek kaynakları ve etiketler — ör. { requiredParameters: ['type'], parameterValues: { type: ['Survey','SurveyPeriodic'] } }"),
|
|
212
|
+
kindOverride: z.enum(['read']).nullable().optional().describe("Yalnız GET uçlarında: çözümlenen 'write' türünü 'read' sayar (iş akışı görev/okundu yazıyorsa); null ezmeyi kaldırır")
|
|
213
|
+
},
|
|
214
|
+
annotations: updates
|
|
215
|
+
}, handle(config, async ({ project, environment, applicationId, microserviceId, ...input }) => {
|
|
216
|
+
guardProduction('assistant_capability_describe', environment);
|
|
217
|
+
const page = await platform.tenant('GET', project, '/assistant/capabilities/page', { environment, query: { applicationId, ids: [microserviceId], pageLength: 1 } });
|
|
218
|
+
const current = (page?.data ?? []).find(row => row.id === microserviceId);
|
|
219
|
+
if (!current)
|
|
220
|
+
throw new Error(`assistant_capability_describe: '${microserviceId}' bu uygulamanın kataloğunda yok — assistant_capability_list ile id'yi ve applicationId'yi doğrulayın.`);
|
|
221
|
+
let hints = input.hints;
|
|
222
|
+
if (input.hints?.parameterSources) {
|
|
223
|
+
const schema = current.inputSchema;
|
|
224
|
+
const properties = schema?.properties ?? schema?.items?.properties;
|
|
225
|
+
const parameterSources = {};
|
|
226
|
+
for (const [parameter, { capability, ...source }] of Object.entries(input.hints.parameterSources)) {
|
|
227
|
+
if (properties && !(parameter in properties))
|
|
228
|
+
throw new Error(`assistant_capability_describe: '${parameter}' bu yeteneğin parametresi değil — kaynak yalnız şemadaki bir parametreye bağlanır (${Object.keys(properties).join(', ')}).`);
|
|
229
|
+
const byId = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(capability);
|
|
230
|
+
const found = await platform.tenant('GET', project, '/assistant/capabilities/page', { environment, query: byId ? { applicationId, ids: [capability], pageLength: 1 } : { applicationId, searchTerm: capability, pageLength: CAPABILITY_PAGE_LIMIT } });
|
|
231
|
+
const row = (found?.data ?? []).find(candidate => (byId ? candidate.id : candidate.slug) === capability);
|
|
232
|
+
if (!row?.id)
|
|
233
|
+
throw new Error(`assistant_capability_describe: '${capability}' bu uygulamanın kataloğunda yok — kaynak aynı uygulamanın bir yeteneğidir; assistant_capability_list { searchTerm } ile slug'ı doğrulayın.`);
|
|
234
|
+
if (String(row.kind ?? '').toLowerCase() !== 'read')
|
|
235
|
+
throw new Error(`assistant_capability_describe: '${row.slug ?? capability}' okuma yeteneği değil (ASI-020) — seçenek kaynağı yalnız okuma yeteneği olur (yazan uç her açılışta yazardı). Listeleyen GET ucunu verin.`);
|
|
236
|
+
parameterSources[parameter] = { capabilityId: row.id, ...source };
|
|
237
|
+
}
|
|
238
|
+
hints = { ...input.hints, parameterSources };
|
|
239
|
+
}
|
|
240
|
+
return platform.tenant('PUT', project, `/assistant/capabilities/${microserviceId}`, { environment, body: mergeCapabilityTexts(current, { ...input, hints }) });
|
|
241
|
+
}));
|
|
242
|
+
server.registerTool('assistant_skill_list', {
|
|
243
|
+
title: 'Becerileri listele',
|
|
244
|
+
description: 'Uygulamanın asistan becerilerini listeler (GET /assistant/skills?applicationId=): ad, slug, tetikleyici, yetenek id\'leri, isReadOnly (hiç yazan yetenek yok), templateItemId (modülden geldi), isModified (yerelde değişti; şablon güncellemesi ezmez).',
|
|
245
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid },
|
|
246
|
+
annotations: readOnly
|
|
247
|
+
}, handle(config, ({ project, environment, applicationId }) => platform.tenant('GET', project, '/assistant/skills', { environment, query: { applicationId } })));
|
|
248
|
+
server.registerTool('assistant_skill_get', {
|
|
249
|
+
title: 'Beceri getir',
|
|
250
|
+
description: 'Tek beceriyi id ya da slug ile getirir (listeyi okuyup gateway seçer; backend\'de tekil uç yoktur).',
|
|
251
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, skill: z.string().min(1).describe('Beceri id ya da slug') },
|
|
252
|
+
annotations: readOnly
|
|
253
|
+
}, handle(config, async ({ project, environment, applicationId, skill }) => {
|
|
254
|
+
const found = (await listSkills(project, environment, applicationId)).find(candidate => candidate.id === skill || candidate.slug === skill);
|
|
255
|
+
if (!found)
|
|
256
|
+
throw new Error(`assistant_skill_get: '${skill}' bu uygulamada yok — assistant_skill_list ile slug ya da id'yi doğrulayın.`);
|
|
257
|
+
return found;
|
|
258
|
+
}));
|
|
259
|
+
server.registerTool('assistant_skill_create', {
|
|
260
|
+
title: 'Beceri tanımla',
|
|
261
|
+
description: "Yeni beceri tanımlar (POST /assistant/skills, gövde = CreateAssistantSkillCommand). Özerklik yalnız 'suggest'tir: asistan yazmaz, onay kartı önerir. Yazan yetenek içeren beceri outputFormat 'suggestion-card' ile kaydedilmelidir (ASI-002). Aynı slug 422 AssistantSkillSlugAlreadyExists, uygulama dışı ya da Excluded yetenek 422. Taslak kaydedilir; runtime yalnız assistant_publish ile yayınlanan kümeyi görür (ASI-005).",
|
|
262
|
+
inputSchema: { project: projectParam, environment: environmentParam, ...skillBody },
|
|
263
|
+
annotations: writes
|
|
264
|
+
}, handle(config, ({ project, environment, ...body }) => {
|
|
265
|
+
guardProduction('assistant_skill_create', environment);
|
|
266
|
+
return platform.tenant('POST', project, '/assistant/skills', { environment, body: { ...body, autonomy: 'suggest' } });
|
|
267
|
+
}));
|
|
268
|
+
server.registerTool('assistant_skill_update', {
|
|
269
|
+
title: 'Beceri güncelle',
|
|
270
|
+
description: 'Beceriyi günceller (PUT /assistant/skills/{id}, gövde = UpdateAssistantSkillCommand; tüm alanlar yeniden gönderilir). Modülden gelen beceri güncellenince isModified:true olur ve sonraki şablon güncellemesi onu ezmez.',
|
|
271
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, ...skillBody },
|
|
272
|
+
annotations: updates
|
|
273
|
+
}, handle(config, ({ project, environment, id, applicationId: _applicationId, ...body }) => {
|
|
274
|
+
guardProduction('assistant_skill_update', environment);
|
|
275
|
+
return platform.tenant('PUT', project, `/assistant/skills/${id}`, { environment, body: { ...body, autonomy: 'suggest' } });
|
|
276
|
+
}));
|
|
277
|
+
server.registerTool('assistant_skill_delete', {
|
|
278
|
+
title: 'Beceri sil',
|
|
279
|
+
description: 'Beceriyi siler (DELETE /assistant/skills/{id}). Geri alınamaz; confirm:true olmadan gateway backend\'i çağırmaz.',
|
|
280
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, confirm: confirmParam },
|
|
281
|
+
annotations: destructive
|
|
282
|
+
}, handle(config, ({ project, environment, id, confirm }) => {
|
|
283
|
+
guardProduction('assistant_skill_delete', environment);
|
|
284
|
+
if (confirm !== true)
|
|
285
|
+
throw new Error("assistant_skill_delete: confirm: true gerekir — silme geri alınamaz; önce assistant_skill_get ile beceriye bakın, sonra confirm: true ile tekrar çağırın.");
|
|
286
|
+
return platform.tenant('DELETE', project, `/assistant/skills/${id}`, { environment });
|
|
287
|
+
}));
|
|
288
|
+
server.registerTool('assistant_scenario_list', {
|
|
289
|
+
title: 'Senaryoları listele',
|
|
290
|
+
description: 'Uygulamanın asistan senaryolarını listeler (GET /assistant/scenarios?applicationId=): kod, soru, beklenen yetenek dizisi/alanlar/kaynak/ret sınıfı, isLeakProbe, lastRunStatus ve lastRunChecksum (koşunun hangi taslak özetine karşı yapıldığı).',
|
|
291
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid },
|
|
292
|
+
annotations: readOnly
|
|
293
|
+
}, handle(config, ({ project, environment, applicationId }) => platform.tenant('GET', project, '/assistant/scenarios', { environment, query: { applicationId } })));
|
|
294
|
+
server.registerTool('assistant_scenario_get', {
|
|
295
|
+
title: 'Senaryo getir',
|
|
296
|
+
description: 'Tek senaryoyu id ya da kod (S-07) ile getirir; son koşular için assistant_scenario_run çıktısına ya da runs:true ile bu araca bakın.',
|
|
297
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, scenario: z.string().min(1).describe('Senaryo id ya da kodu'), runs: z.boolean().optional().describe('true: uygulamanın son koşu kayıtlarından bu senaryoya ait olanlar da döner') },
|
|
298
|
+
annotations: readOnly
|
|
299
|
+
}, handle(config, async ({ project, environment, applicationId, scenario, runs }) => {
|
|
300
|
+
const found = (await listScenarios(project, environment, applicationId)).find(candidate => candidate.id === scenario || candidate.code === scenario);
|
|
301
|
+
if (!found)
|
|
302
|
+
throw new Error(`assistant_scenario_get: '${scenario}' bu uygulamada yok — assistant_scenario_list ile kodu ya da id'yi doğrulayın.`);
|
|
303
|
+
if (runs !== true)
|
|
304
|
+
return found;
|
|
305
|
+
const history = await platform.tenant('GET', project, '/assistant/scenarios/runs', { environment, query: { applicationId } });
|
|
306
|
+
return { ...found, runs: (history?.data ?? []).filter(run => run.scenarioId === found.id) };
|
|
307
|
+
}));
|
|
308
|
+
server.registerTool('assistant_scenario_create', {
|
|
309
|
+
title: 'Senaryo tanımla',
|
|
310
|
+
description: 'Yeni senaryo tanımlar (POST /assistant/scenarios, gövde = CreateAssistantScenarioCommand). Karşılaştırma davranış üzerindendir, cümle değil: beklenen yetenek dizisi birebir sıra (yanlış sıra Order, eksik Missing), beklenen alanlar öneri kartının alt kümesi, beklenen kaynak source olaylarından biri, ret sınıfı refusal olayı, sızıntı provası hiç kayıt dönmemesi. Aynı kod 422; katalogda olmayan beklenen yetenek 422 AssistantScenarioCapabilityUnknown.',
|
|
311
|
+
inputSchema: { project: projectParam, environment: environmentParam, ...scenarioBody },
|
|
312
|
+
annotations: writes
|
|
313
|
+
}, handle(config, ({ project, environment, ...body }) => {
|
|
314
|
+
guardProduction('assistant_scenario_create', environment);
|
|
315
|
+
return platform.tenant('POST', project, '/assistant/scenarios', { environment, body });
|
|
316
|
+
}));
|
|
317
|
+
server.registerTool('assistant_scenario_update', {
|
|
318
|
+
title: 'Senaryo güncelle',
|
|
319
|
+
description: 'Senaryoyu günceller (PUT /assistant/scenarios/{id}, gövde = UpdateAssistantScenarioCommand; tüm alanlar yeniden gönderilir).',
|
|
320
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, ...scenarioBody },
|
|
321
|
+
annotations: updates
|
|
322
|
+
}, handle(config, ({ project, environment, id, applicationId: _applicationId, ...body }) => {
|
|
323
|
+
guardProduction('assistant_scenario_update', environment);
|
|
324
|
+
return platform.tenant('PUT', project, `/assistant/scenarios/${id}`, { environment, body });
|
|
325
|
+
}));
|
|
326
|
+
server.registerTool('assistant_scenario_delete', {
|
|
327
|
+
title: 'Senaryo sil',
|
|
328
|
+
description: 'Senaryoyu ve koşu geçmişini siler (DELETE /assistant/scenarios/{id}). Geri alınamaz; confirm:true olmadan gateway backend\'i çağırmaz.',
|
|
329
|
+
inputSchema: { project: projectParam, environment: environmentParam, id: guid, confirm: confirmParam },
|
|
330
|
+
annotations: destructive
|
|
331
|
+
}, handle(config, ({ project, environment, id, confirm }) => {
|
|
332
|
+
guardProduction('assistant_scenario_delete', environment);
|
|
333
|
+
if (confirm !== true)
|
|
334
|
+
throw new Error("assistant_scenario_delete: confirm: true gerekir — silme geri alınamaz; önce assistant_scenario_get ile bakın, sonra confirm: true ile tekrar çağırın.");
|
|
335
|
+
return platform.tenant('DELETE', project, `/assistant/scenarios/${id}`, { environment });
|
|
336
|
+
}));
|
|
337
|
+
server.registerTool('assistant_scenario_run', {
|
|
338
|
+
title: 'Senaryoları koş',
|
|
339
|
+
description: "Seçili (ya da hepsi) senaryoları gerçek modelle koşar (POST /assistant/scenarios/run, SSE; gateway akışı sonuna kadar okur ve özetler): geçme / yetenek isabeti / sızıntı sayıları ve senaryo başına durum + neden. Production ortamında koşmaz (error Production). Yetki kaynağı 'application-middleware' ise actorToken (hedef kullanıcının jetonu, saklanmaz) gerekir; yoksa senaryolar Skipped(NoActorToken). Sağlayıcı anahtarı yoksa 503. Koşu sonucu assistant_publish kapısının girdisidir (ASI-006).",
|
|
340
|
+
inputSchema: {
|
|
341
|
+
project: projectParam,
|
|
342
|
+
environment: environmentParam,
|
|
343
|
+
applicationId: guid,
|
|
344
|
+
scenarioIds: z.array(guid).optional().describe('Boş ya da yok: uygulamanın bütün senaryoları'),
|
|
345
|
+
actorToken: z.string().optional().describe("'application-middleware' kaynağında hedef kullanıcının jetonu (Bearer); gateway iletir, saklamaz"),
|
|
346
|
+
actorCookie: z.string().optional().describe("Uygulama kimliği çerezle taşınıyorsa (ör. qm: 'SY_Token=…') tam çerez çifti; actorToken yerine geçer, gateway iletir, saklamaz"),
|
|
347
|
+
actor: actorParam,
|
|
348
|
+
actorCookieName: actorCookieNameParam
|
|
349
|
+
},
|
|
350
|
+
annotations: writes
|
|
351
|
+
}, handle(config, async ({ project, environment, applicationId, scenarioIds, actorToken, actorCookie, actor, actorCookieName }) => summarizeAssistantRun(await streamEvents(project, environment, '/assistant/scenarios/run', { applicationId, scenarioIds: scenarioIds ?? [], ...await resolveActorFields(platform, project, environment, { actor, actorCookieName, actorToken, actorCookie }) }))));
|
|
352
|
+
server.registerTool('assistant_simulate', {
|
|
353
|
+
title: 'Asistanı simüle et',
|
|
354
|
+
description: "Tek girdiyi seçilen kişinin gözünden koşar (POST /assistant/simulate, SSE; gateway akışı özetler). Varsayılan taslak becerilerle; published:true son kullanıcının bugün gördüğü YAYINDAKİ beceri setiyle koşar (hiç yayın yoksa error NoPublished, taslağa düşmez). history ile çok turlu konuşma denenir (önceki turlar; senaryo kaydı tek turludur). Yerleşik araçlar her beceride hazırdır: kayit_say (yalnız sayı sorulduğunda listeyi sayfa sayfa sayar), sayfa_bul (menüden sayfa yolu + link bloğu). Özet: beceri, çağrılan yetenekler ve sonuçları (reddedilenler dahil), kaynaklar, öneri kartı (suggestionId, slug, httpMethod, arguments, missing — uygulamak microservice_execute(slug, arguments) ile ve yalnız kullanıcı isterse), kayıt blokları (sample: ilk 20 satırın id/label/status/statusLabel/href'i; link bloğunda path + href), ret sınıfı, cevap metni, süre/token. Production ortamında koşmaz. Platform kaynağında actorUserId impersonation ile (production dışı, süper yönetici jetonu); 'application-middleware' kaynağında actorToken zorunlu (error NoActorToken).",
|
|
355
|
+
inputSchema: {
|
|
356
|
+
project: projectParam,
|
|
357
|
+
environment: environmentParam,
|
|
358
|
+
applicationId: guid,
|
|
359
|
+
message: z.string().min(1),
|
|
360
|
+
actorUserId: guid.optional(),
|
|
361
|
+
actorToken: z.string().optional(),
|
|
362
|
+
actorCookie: z.string().optional().describe("Uygulama kimliği çerezle taşınıyorsa tam çerez çifti (ör. 'SY_Token=…'); actorToken yerine geçer"),
|
|
363
|
+
published: z.boolean().optional().describe('true: taslak yerine yayındaki beceri setiyle koşar (son kullanıcının göreceği asistan)'),
|
|
364
|
+
history: historyParam,
|
|
365
|
+
actor: actorParam,
|
|
366
|
+
actorCookieName: actorCookieNameParam,
|
|
367
|
+
context: contextParam
|
|
368
|
+
},
|
|
369
|
+
annotations: writes
|
|
370
|
+
}, handle(config, async ({ project, environment, applicationId, message, actorUserId, actorToken, actorCookie, published, history, actor, actorCookieName, context }) => summarizeAssistantRun(await streamEvents(project, environment, '/assistant/simulate', { applicationId, message, actorUserId: actorUserId ?? null, ...await resolveActorFields(platform, project, environment, { actor, actorCookieName, actorToken, actorCookie }), usePublished: published === true, history: history ?? [], ...(context ? { context } : {}) }))));
|
|
371
|
+
server.registerTool('assistant_publish', {
|
|
372
|
+
title: 'Asistanı yayına al',
|
|
373
|
+
description: "Taslak beceri kümesini yayınlar (POST /assistant/publish/stream, SSE; gateway akışı sonuna kadar okur): runtime paneli yalnız yayınlanan kümeyi kullanır, taslak değişiklikleri yayına kadar görünmez (ASI-005). Özet: stages[] (catalog → gate → package → live, her biri running/done/failed; gate'in detail'ında nedenler), published (yayın sonrası durum: publishedAt, sayılar) ve hata varsa error. Kapı kapalıysa gate failed + error AssistantPublishGateClosed ve nedenler: scenario-failed (yeşil olmayan senaryo), stale (koşular taslaktan eski — açıklama ya da beceri değişti, yeniden koşun), leak (sızıntı provası kaldı), description (kullanılan yeteneğin açıklaması yok). check:true yalnız kapı durumunu okur (GET /assistant/publish), yayınlamaz. Production'da yayınlamaz (ASI-013).",
|
|
374
|
+
inputSchema: { project: projectParam, environment: environmentParam, applicationId: guid, check: z.boolean().optional().describe('true: yayınlamadan kapı durumunu ve sayıları döner') },
|
|
375
|
+
annotations: writes
|
|
376
|
+
}, handle(config, async ({ project, environment, applicationId, check }) => {
|
|
377
|
+
if (check === true)
|
|
378
|
+
return platform.tenant('GET', project, '/assistant/publish', { environment, query: { applicationId } });
|
|
379
|
+
guardProduction('assistant_publish', environment);
|
|
380
|
+
return summarizeAssistantRun(await streamEvents(project, environment, '/assistant/publish/stream', { applicationId }));
|
|
381
|
+
}));
|
|
382
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
1
2
|
import { guid, readOnly, writes } from './common.js';
|
|
2
3
|
import { handle } from '../platform/responses.js';
|
|
3
4
|
// Koşu 42 S-21: promote yayınlanmamış taslağı taşımaz (sözleşme) ve saga bunu hiçbir yerde söylemez.
|
|
@@ -26,8 +27,13 @@ export function registerDeploymentTools(server, platform) {
|
|
|
26
27
|
const config = platform.config;
|
|
27
28
|
server.registerTool('deployment_start', {
|
|
28
29
|
title: 'Deployment başlat',
|
|
29
|
-
description: 'Projeyi kaynak environment\'tan hedef environment\'a deploy eder. Asenkron bir saga\'dır: sonuç bu yanıtta bitmez, deployment_status ile izleyin. Environment id\'lerini project_get çıktısından alın. BPM: süreç tanımları ve iş takvimleri taşınır — hedefte farklı aktif model yeni sürüm olarak yayınlanır (production\'da gelişte aktif), hedefin uyarlaması korunur; uyarlama, örnek, görev ve defter taşınmaz; kaynakta olmayan tanım silinir, açık örneği varsa emekliye ayrılır. KAYNAK YALNIZ DEVELOPMENT\'TIR: dal ya da production kaynak verilirse 400 (CantDeployFromBranchException) — dalı önce development\'a merge edin. ÖN DENETİM: deploy başlamadan production verisi denetlenir; veriyi bozacak ya da silecek her değişiklik 400 (DeploymentDataRiskException) ile listelenir ve deploy BAŞLAMAZ — biçim \'tablo.alan: tür (satır)\', türler not-null | unique | field-removed | table-removed | localization-move. YALNIZ TANIM TAŞINIR: tablo satırları ancak tabloda recordTransferMode (\'all\' | \'specific\' + transferRowIds) açıksa gider, çeviri ve junction satırları ana tabloyla birlikte. Kayıt taşıması \'all\' olan tabloda production development\'ın AYNASIDIR (kaynakta olmayan satır ve çevirisi silinir). DOSYA yalnız id referansıyla gider (file tipli prop { type:\'file\', ref:<fileId> } ya da taşınan satırın dosya sütunu); sayfaya düz yazılan /d/ serve URL\'si /p/\'ye çevrilir ama dosya kopyalanmaz, production\'da 404 döner — platform_guide(\'deployment\'). Microfunction\'ın yalnız YAYINLANMIŞ kodu taşınır ve hedefte yayınlanır; hiç yayınlanmamış olan hedefte taslak kalır. Zamanlanmış işin duraklatma durumu taşınır. YAYINLANMAMIŞ TASLAK TAŞINMAZ: kaynakta taslağı olan süreçler yanıtta unpublishedDrafts ({ slug, name, draftVersionNumber, activeVersionNumber }) ve note ile listelenir; hedefte aktif sürüm kalır. Taşımak için taslağı kaynakta yayınlayın (requireGreenScenarios açıksa önce senaryoları taslak üzerinde koşun), sonra yeniden deploy edin.',
|
|
30
|
-
inputSchema: {
|
|
30
|
+
description: 'Projeyi kaynak environment\'tan hedef environment\'a deploy eder. Asenkron bir saga\'dır: sonuç bu yanıtta bitmez, deployment_status ile izleyin. Environment id\'lerini project_get çıktısından alın. BPM: süreç tanımları ve iş takvimleri taşınır — hedefte farklı aktif model yeni sürüm olarak yayınlanır (production\'da gelişte aktif), hedefin uyarlaması korunur; uyarlama, örnek, görev ve defter taşınmaz; kaynakta olmayan tanım silinir, açık örneği varsa emekliye ayrılır. KAYNAK YALNIZ DEVELOPMENT\'TIR: dal ya da production kaynak verilirse 400 (CantDeployFromBranchException) — dalı önce development\'a merge edin. ÖN DENETİM: deploy başlamadan production verisi denetlenir; veriyi bozacak ya da silecek her değişiklik 400 (DeploymentDataRiskException) ile listelenir ve deploy BAŞLAMAZ — biçim \'tablo.alan: tür (satır)\', türler not-null | unique | field-removed | table-removed | localization-move. YALNIZ TANIM TAŞINIR: tablo satırları ancak tabloda recordTransferMode (\'all\' | \'specific\' + transferRowIds) açıksa gider, çeviri ve junction satırları ana tabloyla birlikte. Kayıt taşıması \'all\' olan tabloda production development\'ın AYNASIDIR (kaynakta olmayan satır ve çevirisi silinir). DOSYA yalnız id referansıyla gider (file tipli prop { type:\'file\', ref:<fileId> } ya da taşınan satırın dosya sütunu); sayfaya düz yazılan /d/ serve URL\'si /p/\'ye çevrilir ama dosya kopyalanmaz, production\'da 404 döner — platform_guide(\'deployment\'). Microfunction\'ın yalnız YAYINLANMIŞ kodu taşınır ve hedefte yayınlanır; hiç yayınlanmamış olan hedefte taslak kalır. Zamanlanmış işin duraklatma durumu taşınır. YAYINLANMAMIŞ TASLAK TAŞINMAZ: kaynakta taslağı olan süreçler yanıtta unpublishedDrafts ({ slug, name, draftVersionNumber, activeVersionNumber }) ve note ile listelenir; hedefte aktif sürüm kalır. Taşımak için taslağı kaynakta yayınlayın (requireGreenScenarios açıksa önce senaryoları taslak üzerinde koşun), sonra yeniden deploy edin. DELTA (varsayılan): yalnız içerik özeti production\'dakinden farklı tanımlar taşınır, kaynakta olmayan tanım silinir; entity, rol, erişim politikası, bileşen, dosya, süreç tanımı, organizasyon yapısı ve tablo satırları her deploy\'da tam taşınır; klasör silme taşınmaz. Kaynağın ya da production\'ın özeti güvenilir değilse (son tamamlanmış özet denetimi güncel biçimde bir fill ya da farksız bir check değilse) deploy kendiliğinden tam koşar; fullMode:true her tanımı taşır. Hangi kipte koştuğu ve modül başına sayılar deployment_status yanıtında (mode, counters).',
|
|
31
|
+
inputSchema: {
|
|
32
|
+
projectId: guid,
|
|
33
|
+
sourceEnvironmentId: guid,
|
|
34
|
+
targetEnvironmentId: guid,
|
|
35
|
+
fullMode: z.boolean().optional().describe("true: her tanımı tam taşır. Boş ya da false: delta — yalnız production'dan farklı tanımlar; özet güvenilir değilse platform yine tam taşır")
|
|
36
|
+
},
|
|
31
37
|
annotations: writes
|
|
32
38
|
}, handle(config, async ({ projectId, ...body }) => {
|
|
33
39
|
const unpublishedDrafts = await readUnpublishedDrafts(platform, projectId, body.sourceEnvironmentId).catch(() => undefined);
|
|
@@ -45,7 +51,7 @@ export function registerDeploymentTools(server, platform) {
|
|
|
45
51
|
}));
|
|
46
52
|
server.registerTool('deployment_status', {
|
|
47
53
|
title: 'Deployment durumu',
|
|
48
|
-
description: "Projenin son deployment durumunu döner. 'projectId' ZORUNLUDUR (project_list/project_get'ten alın) — verilmezse çağrı MCP şema katmanında reddedilir, platforma hiç gitmez. Hiç deployment yapılmamış projede yanıt BOŞ gelir; o durumda tool 'hiç deployment kaydı yok' bilgisini açıkça döner, yani boş yanıtı 'tool çalışmadı' diye okumayın. Durum satırı modül başına ayrılmaz: süreç tanımları ve iş takvimleri sayfa ve workflow'lardan sonra aynı saga adımında aktarılır; hedefte çalışamayan (eksik workflow/entity/rol) tanım aktarımı DefinitionInvalid ile düşürür. type 'error' ise 'error' alanı düşüş nedenini taşır (DefinitionInvalid'de bulgular 'yol: mesaj | …' olarak eklenir); aktarım atomiktir, hedef değişmemiştir.",
|
|
54
|
+
description: "Projenin son deployment durumunu döner. 'projectId' ZORUNLUDUR (project_list/project_get'ten alın) — verilmezse çağrı MCP şema katmanında reddedilir, platforma hiç gitmez. Hiç deployment yapılmamış projede yanıt BOŞ gelir; o durumda tool 'hiç deployment kaydı yok' bilgisini açıkça döner, yani boş yanıtı 'tool çalışmadı' diye okumayın. Durum satırı modül başına ayrılmaz: süreç tanımları ve iş takvimleri sayfa ve workflow'lardan sonra aynı saga adımında aktarılır; hedefte çalışamayan (eksik workflow/entity/rol) tanım aktarımı DefinitionInvalid ile düşürür. type 'error' ise 'error' alanı düşüş nedenini taşır (DefinitionInvalid'de bulgular 'yol: mesaj | …' olarak eklenir); aktarım atomiktir, hedef değişmemiştir. type 'completed-with-mismatch': deploy tamamlandı, kayıtlar production'a yazıldı, ama ardından koşan özet denetimi production'da fark buldu; sonraki deploy otomatik tam (full) koşar. Yanıt mode (full | delta: deploy'un gerçekte koştuğu kip) ve counters (delta'da modül başına added/updated/deleted) taşır.",
|
|
49
55
|
inputSchema: { projectId: guid },
|
|
50
56
|
annotations: readOnly
|
|
51
57
|
}, handle(config, async ({ projectId }) => {
|
|
@@ -455,7 +455,7 @@ export function registerMicroserviceTools(server, platform) {
|
|
|
455
455
|
const requestHeaders = { ...runIdentity(anonymous, member, cookies, headers), ...eye, ...languageHeader(language, notes) };
|
|
456
456
|
const bound = bindCallParameters(method, parameters, query, body, notes);
|
|
457
457
|
assertFilterOperators('microservice_execute', bound);
|
|
458
|
-
const raw = await platform.tenantRaw(method, project, executePath('m', application, microservice, routeParams), { environment, query: bound.query, body: bound.body, headers: requestHeaders, anonymous, mirrorAuthorizationCookie: true });
|
|
458
|
+
const raw = await platform.tenantRaw(method, project, executePath('m', application, microservice, routeParams), { environment, query: bound.query, body: bound.body, headers: requestHeaders, anonymous, mirrorAuthorizationCookie: true, noNetworkRetry: true });
|
|
459
459
|
return withNormalization({ ...raw, headers: headersForMode(raw.headers, includeHeaders ?? 'essential') }, notes);
|
|
460
460
|
}));
|
|
461
461
|
server.registerTool('microservice_debug', {
|
package/dist/tools/mobile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { environmentParam, guid, pagingParams, projectParam, readOnly, writes } from './common.js';
|
|
2
|
+
import { environmentParam, guid, pagingParams, projectParam, readOnly, writes, templateSubscriptionLock } from './common.js';
|
|
3
3
|
import { fetchHost, pageUpdateBody, unwrap } from './screens.js';
|
|
4
4
|
import { fetchCatalogSlugs } from './components.js';
|
|
5
5
|
import { mobileTabsWarnings } from './applications.js';
|
|
@@ -144,7 +144,7 @@ export function registerMobileTools(server, platform) {
|
|
|
144
144
|
}, handle(config, ({ project, environment, applicationId, id, ...query }) => platform.tenant('GET', project, id ? `/push/notifications/${applicationId}/${id}` : `/push/notifications/${applicationId}`, { environment, query })));
|
|
145
145
|
server.registerTool('mobile_app_create', {
|
|
146
146
|
title: 'Mobil uygulamayı kabuğuyla kur',
|
|
147
|
-
description: "Mobil uygulamayı (type:'mobile'), kabuk layout'unu (app-bar + slot; drawer:true ise çekmece widget'ı), sayfalarını ve isteğe bağlı sekme ayarını (properties.mobile.tabs) TEK çağrıda kurar. Sıra: application_create → layout_create → page_create (sayfalar layoutId ile doğar) → backend'in açtığı ana sayfa seçilen ana sayfaya dönüştürülür (yetim sayfa kalmaz) → application_update (yalnız tabs verildiyse). tabs yoksa gezinme yığındır. dryRun:true planı döner, hiçbir şey yazmaz. Platformda işlem yoktur: hata olursa yanıt completed (kurulanlar), failed (düşen adım) ve remaining (kalanlar) taşır — kalanları tekil araçlarla tamamlayın. Kabuk bileşenleri (app-bar, slot) mobil katalogda yoksa layout atlanır ve uyarı döner. Rehber: platform_guide('mobile').",
|
|
147
|
+
description: "Mobil uygulamayı (type:'mobile'), kabuk layout'unu (app-bar + slot; drawer:true ise çekmece widget'ı), sayfalarını ve isteğe bağlı sekme ayarını (properties.mobile.tabs) TEK çağrıda kurar. Sıra: application_create → layout_create → page_create (sayfalar layoutId ile doğar) → backend'in açtığı ana sayfa seçilen ana sayfaya dönüştürülür (yetim sayfa kalmaz) → application_update (yalnız tabs verildiyse). tabs yoksa gezinme yığındır. dryRun:true planı döner, hiçbir şey yazmaz. Platformda işlem yoktur: hata olursa yanıt completed (kurulanlar), failed (düşen adım) ve remaining (kalanlar) taşır — kalanları tekil araçlarla tamamlayın. Kabuk bileşenleri (app-bar, slot) mobil katalogda yoksa layout atlanır ve uyarı döner. Rehber: platform_guide('mobile'). " + templateSubscriptionLock,
|
|
148
148
|
inputSchema: {
|
|
149
149
|
project: projectParam,
|
|
150
150
|
environment: environmentParam,
|
package/dist/tools/projects.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive } from './common.js';
|
|
1
|
+
import { guid, projectParam, environmentParam, pagingParams, readOnly, writes, updates, destructive, templateSubscriptionLock } from './common.js';
|
|
2
2
|
import { normalizeLanguageCode, normalizeLanguageCodes } from '../knowledge/languageCodes.js';
|
|
3
3
|
import { projectCoverageGaps } from '../knowledge/coverage.js';
|
|
4
4
|
import { withNormalization } from '../knowledge/normalize.js';
|
|
@@ -266,7 +266,7 @@ export function registerProjectTools(server, platform) {
|
|
|
266
266
|
}));
|
|
267
267
|
server.registerTool('project_update', {
|
|
268
268
|
title: 'Proje güncelle',
|
|
269
|
-
description: "Proje adını, slug'ını, temasını ve dil ayarlarını günceller. languages/defaultLanguage verilmezse mevcut değerlerine dokunulmaz. SLUG: vermezseniz gateway projeyi okuyup MEVCUT slug'ı gövdeye kendisi koyar ve bunu gatewayNormalized ile bildirir — platform slug'ı ADDAN yeniden üretiyor ve slug runtime rotasının parçası olduğu için (/{projectSlug}/{application}/{page}) tek çağrı bütün sayfa URL'lerini ve DEFAULT_PROJECT çözümlemesini kırıyordu (ölçüldü 2026-09-05). Slug'ı gerçekten değiştirmek istiyorsanız açıkça yazın; yanıt o zaman rotanın da değiştiğini söyler. LoaderImage bu tool'da desteklenmez.",
|
|
269
|
+
description: "Proje adını, slug'ını, temasını ve dil ayarlarını günceller. languages/defaultLanguage verilmezse mevcut değerlerine dokunulmaz. SLUG: vermezseniz gateway projeyi okuyup MEVCUT slug'ı gövdeye kendisi koyar ve bunu gatewayNormalized ile bildirir — platform slug'ı ADDAN yeniden üretiyor ve slug runtime rotasının parçası olduğu için (/{projectSlug}/{application}/{page}) tek çağrı bütün sayfa URL'lerini ve DEFAULT_PROJECT çözümlemesini kırıyordu (ölçüldü 2026-09-05). Slug'ı gerçekten değiştirmek istiyorsanız açıkça yazın; yanıt o zaman rotanın da değiştiğini söyler. LoaderImage bu tool'da desteklenmez. " + templateSubscriptionLock,
|
|
270
270
|
inputSchema: {
|
|
271
271
|
id: guid,
|
|
272
272
|
name: z.string().min(1).optional().describe('Verilmezse mevcut ad korunur — yalnız dil ayarını değiştirmek için adı tekrar göndermeniz gerekmez'),
|
package/dist/tools/templates.js
CHANGED
|
@@ -104,7 +104,7 @@ export function registerTemplateTools(server, platform) {
|
|
|
104
104
|
}));
|
|
105
105
|
server.registerTool('template_check_updates', {
|
|
106
106
|
title: 'Şablon güncellemesi var mı',
|
|
107
|
-
description: 'Şablondan kurulmuş projenin kaynak şablonunda yeni sürüm var mı kontrol eder.',
|
|
107
|
+
description: 'Şablondan kurulmuş projenin kaynak şablonunda yeni sürüm var mı kontrol eder. Yeni sürüm varsa added / changed / removed, güncellemenin kuracağı eklenen, değişen ve silinen kalem sayısıdır; null ise kurulu sürümün manifesti yok ve güncelleme bütün kalemleri yeniden kurar. Üçü 0 olan sürümde, abonenin şablondan kurulu kayıtları yerindeyse güncelleme kurucu koşturmadan yalnız sürümü ilerletir; eksik kayıt varsa onu yeniden kurar.',
|
|
108
108
|
inputSchema: { projectId: guid },
|
|
109
109
|
annotations: readOnly
|
|
110
110
|
}, handle(config, ({ projectId }) => platform.org('GET', `/projects/${projectId}/templates/check-for-updates`)));
|
package/dist/tools/themes.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { guid, projectParam, readOnly, updates } from './common.js';
|
|
1
|
+
import { guid, projectParam, readOnly, updates, templateSubscriptionLock } from './common.js';
|
|
2
2
|
import { diffCssVars, mergeRadiusScale, mergeSpacingScale, mergeTypeScale, resolveTheme } from '../knowledge/themeResolve.js';
|
|
3
3
|
import { diffThemePaths, mergeTheme, normalizeTheme, readProjectTheme, summarizeTheme, themeInput, unsetThemePaths } from '../knowledge/themeSchema.js';
|
|
4
4
|
import { componentPresetTypeWarnings, themeInputWarnings, unsetWarnings } from '../knowledge/themeRules.js';
|
|
@@ -144,7 +144,7 @@ export function registerThemeTools(server, platform) {
|
|
|
144
144
|
}));
|
|
145
145
|
server.registerTool('theme_update', {
|
|
146
146
|
title: 'Proje temasını güncelle',
|
|
147
|
-
description: "Temayı KISMİ günceller: kaydı okur, verdiğiniz alanları derin birleştirir, normalize eder, lint'ten geçirir ve yazar — bir rengi değiştirmek preset'leri ve bileşen preset'lerini düşürmez. Yanıt hangi yolların değiştiğini (changed) ve hangi CSS değişkeninin hangi değerden hangi değere gittiğini (cssVarsChanged) söyler; dryRun ile yazmadan aynı yanıtı alırsınız. DİKKAT: tema PROJE kaydıdır — değişiklik BÜTÜN ortamların runtime'ına iner (production dâhil) ve Studio tema paneli açık ve kaydedilmemişse panelin sonraki Save'i bu yazmayı ezer.",
|
|
147
|
+
description: "Temayı KISMİ günceller: kaydı okur, verdiğiniz alanları derin birleştirir, normalize eder, lint'ten geçirir ve yazar — bir rengi değiştirmek preset'leri ve bileşen preset'lerini düşürmez. Yanıt hangi yolların değiştiğini (changed) ve hangi CSS değişkeninin hangi değerden hangi değere gittiğini (cssVarsChanged) söyler; dryRun ile yazmadan aynı yanıtı alırsınız. DİKKAT: tema PROJE kaydıdır — değişiklik BÜTÜN ortamların runtime'ına iner (production dâhil) ve Studio tema paneli açık ve kaydedilmemişse panelin sonraki Save'i bu yazmayı ezer. " + templateSubscriptionLock,
|
|
148
148
|
inputSchema: {
|
|
149
149
|
project: projectParam,
|
|
150
150
|
theme: themeInput.optional().describe("Yazılacak alanlar — KISMİ. Nesneler derin birleşir, dizi/ilkel değer yerine geçer. Tanınmayan anahtar (ör. 'pallete') istek gitmeden reddedilir: platform temayı doğrulamayan bir jsonb blob'u olarak saklar ve yazım hatası kayıtta sessizce yaşardı"),
|
package/package.json
CHANGED