@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
package/.env.example
CHANGED
|
@@ -78,3 +78,8 @@ TENANT_SWAGGER_URL=
|
|
|
78
78
|
MODULES=
|
|
79
79
|
|
|
80
80
|
MCP_PORT=5100
|
|
81
|
+
|
|
82
|
+
# Uygulama asistanı (Anthropic). docker-compose*.yml Assistant__ApiKey / Assistant__WorkspaceId buradan okur;
|
|
83
|
+
# anahtar appsettings.json'a ya da compose dosyasına yazılmaz. Kullanıcı anahtarı (sk-ant-usr-…) çalışma alanı kimliği ister.
|
|
84
|
+
ASSISTANT_API_KEY=
|
|
85
|
+
ANTHROPIC_WORKSPACE_ID=
|
package/dist/config.js
CHANGED
|
@@ -48,6 +48,7 @@ export const moduleNames = [
|
|
|
48
48
|
"caseRuntime",
|
|
49
49
|
"module",
|
|
50
50
|
"mobile",
|
|
51
|
+
"assistant",
|
|
51
52
|
];
|
|
52
53
|
export const modulePresets = {
|
|
53
54
|
core: [
|
|
@@ -103,6 +104,7 @@ export const modulePresets = {
|
|
|
103
104
|
"revision",
|
|
104
105
|
"connectData",
|
|
105
106
|
"mobile",
|
|
107
|
+
"assistant",
|
|
106
108
|
],
|
|
107
109
|
admin: ["platform", "template", "organization", "deployment", "environment", "merge", "activity"],
|
|
108
110
|
};
|
package/dist/knowledge/access.js
CHANGED
|
@@ -53,7 +53,7 @@ export function systemManagedEntity(ref) {
|
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* İzin kataloğu (`AccessPermissionConstants`, b-22 — 43 slug; BPM b-01 beşini ekledi, 2026-09-24'te
|
|
56
|
-
* `automation.trigger` çıktı: otomasyon yetki kataloğunda değil — K-2). `supportsScope` altı entity
|
|
56
|
+
* `automation.trigger` çıktı: otomasyon yetki kataloğunda değil — K-2; 2026-10-22'de tür kapsamlı `assistant.use` girdi). `supportsScope` altı entity
|
|
57
57
|
* eyleminde ve `process.view-all`'da, `supportsFieldAccess` yalnız `field.read`'te. Katalog yalnız kompleks projede
|
|
58
58
|
* tohumlanır; kaynağı `access_permission_list`'tir, bu kopya istek gitmeden denetim içindir.
|
|
59
59
|
*/
|
|
@@ -66,10 +66,11 @@ export const accessPermissionSlugs = [
|
|
|
66
66
|
'file.read', 'file.upload', 'file.delete', 'folder.read', 'folder.upload', 'folder.delete',
|
|
67
67
|
'organization-unit.read', 'organization-unit.manage', 'organization-unit.assign',
|
|
68
68
|
'system.access.manage', 'system.access.explain', 'system.user.read', 'system.user.manage',
|
|
69
|
-
'process.start', 'process.manage', 'process.view-all', 'task.complete', 'task.reassign'
|
|
69
|
+
'process.start', 'process.manage', 'process.view-all', 'task.complete', 'task.reassign',
|
|
70
|
+
'assistant.use'
|
|
70
71
|
];
|
|
71
72
|
export const scopedPermissions = new Set(['entity.read', 'entity.update', 'entity.delete', 'entity.export', 'entity.reveal', 'entity.reassign', 'process.view-all']);
|
|
72
|
-
export const accessResourceTypes = ['entity', 'field', 'row', 'microservice', 'page', 'workflow', 'automation', 'ui-flow', 'function', 'microfunction', 'query', 'view', 'file', 'folder', 'organization-unit', 'system', 'process', 'task'];
|
|
73
|
+
export const accessResourceTypes = ['entity', 'field', 'row', 'microservice', 'page', 'workflow', 'automation', 'ui-flow', 'function', 'microfunction', 'query', 'view', 'file', 'folder', 'organization-unit', 'system', 'process', 'task', 'assistant'];
|
|
73
74
|
export const accessScopes = ['own', 'unit', 'unit-tree', 'subordinates', 'function', 'company', 'all'];
|
|
74
75
|
const knownPermissions = new Set(accessPermissionSlugs);
|
|
75
76
|
const conditionSubjects = new Set(['userId', 'unitIds', 'positionIds', 'companyIds', 'roleSlugs', 'functionCodes']);
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { maskSecrets } from '../platform/secrets.js';
|
|
2
|
+
/** `event: x\ndata: {...}\n\n` bloklarını olay listesine çevirir; bozuk bir veri satırı atlanır */
|
|
3
|
+
export function parseSseEvents(text) {
|
|
4
|
+
const events = [];
|
|
5
|
+
for (const block of text.split(/\r?\n\r?\n/)) {
|
|
6
|
+
let type = 'message';
|
|
7
|
+
const dataLines = [];
|
|
8
|
+
for (const line of block.split(/\r?\n/)) {
|
|
9
|
+
if (line.startsWith('event:'))
|
|
10
|
+
type = line.slice(6).trim();
|
|
11
|
+
else if (line.startsWith('data:'))
|
|
12
|
+
dataLines.push(line.slice(5).trim());
|
|
13
|
+
}
|
|
14
|
+
if (dataLines.length === 0)
|
|
15
|
+
continue;
|
|
16
|
+
try {
|
|
17
|
+
events.push({ type, data: JSON.parse(dataLines.join('\n')) });
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
events.push({ type, data: { raw: dataLines.join('\n') } });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return events;
|
|
24
|
+
}
|
|
25
|
+
const BLOCK_SAMPLE_ROWS = 20;
|
|
26
|
+
const BLOCK_ROW_FIELDS = ['id', 'label', 'status', 'statusLabel', 'href'];
|
|
27
|
+
/** Senaryo koşusunun ve simülasyonun olay akışını tek özet nesneye indirir; metin yalnız cevap olayından gelir */
|
|
28
|
+
export function summarizeAssistantRun(events) {
|
|
29
|
+
const summary = {};
|
|
30
|
+
const scenarios = [];
|
|
31
|
+
const calls = [];
|
|
32
|
+
const sources = [];
|
|
33
|
+
let text = '';
|
|
34
|
+
for (const event of events) {
|
|
35
|
+
const data = event.data;
|
|
36
|
+
switch (event.type) {
|
|
37
|
+
case 'scenario':
|
|
38
|
+
scenarios.push({ code: String(data.code ?? ''), status: String(data.status ?? ''), reason: data.reason ?? null, calledCapabilities: data.calledCapabilities ?? [], durationMs: Number(data.durationMs ?? 0), ...(data.answer !== undefined ? { answer: data.answer ?? null } : {}), ...(data.refusal !== undefined ? { refusal: data.refusal ?? null } : {}) });
|
|
39
|
+
break;
|
|
40
|
+
case 'skill':
|
|
41
|
+
summary.skill = { slug: String(data.slug ?? ''), name: String(data.name ?? '') };
|
|
42
|
+
break;
|
|
43
|
+
case 'tool_start':
|
|
44
|
+
calls.push({ slug: String(data.slug ?? ''), statusCode: null, bytes: 0, denied: false, ...(data.arguments != null ? { arguments: maskSecrets(data.arguments).payload } : {}) });
|
|
45
|
+
break;
|
|
46
|
+
case 'tool_result': {
|
|
47
|
+
const call = calls.find(candidate => candidate.slug === String(data.slug ?? '') && candidate.statusCode === null);
|
|
48
|
+
if (call)
|
|
49
|
+
Object.assign(call, { statusCode: typeof data.statusCode === 'number' ? data.statusCode : null, bytes: Number(data.bytes ?? 0), denied: data.denied === true });
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
case 'source':
|
|
53
|
+
sources.push(String(data.slug ?? ''));
|
|
54
|
+
break;
|
|
55
|
+
case 'suggestion': {
|
|
56
|
+
const args = data.arguments ?? {};
|
|
57
|
+
const choices = Array.isArray(data.choices) ? data.choices : [];
|
|
58
|
+
const properties = (data.inputSchema?.properties) ?? {};
|
|
59
|
+
summary.suggestion = {
|
|
60
|
+
suggestionId: String(data.suggestionId ?? ''),
|
|
61
|
+
slug: String(data.slug ?? ''),
|
|
62
|
+
httpMethod: String(data.httpMethod ?? ''),
|
|
63
|
+
arguments: args,
|
|
64
|
+
missing: data.missing ?? [],
|
|
65
|
+
fields: Object.keys(args),
|
|
66
|
+
choices: choices.map(choice => ({ field: String(choice.field ?? ''), total: Number(choice.total ?? 0), searchable: choice.searchable === true, preselected: typeof choice.preselected === 'string' ? choice.preselected : null, options: (choice.options ?? []).map(option => String(option.label ?? '')) })),
|
|
67
|
+
choicesError: data.choicesError ?? {},
|
|
68
|
+
display: data.display ?? {},
|
|
69
|
+
titles: Object.fromEntries(Object.entries(properties).filter(([, property]) => typeof property?.title === 'string').map(([key, property]) => [key, String(property.title)])),
|
|
70
|
+
formHref: typeof data.formHref === 'string' ? data.formHref : null
|
|
71
|
+
};
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case 'refusal':
|
|
75
|
+
summary.refusal = String(data.kind ?? '');
|
|
76
|
+
break;
|
|
77
|
+
case 'text':
|
|
78
|
+
text += String(data.text ?? '');
|
|
79
|
+
break;
|
|
80
|
+
case 'clarify':
|
|
81
|
+
summary.clarify = String(data.question ?? '');
|
|
82
|
+
break;
|
|
83
|
+
case 'usage':
|
|
84
|
+
summary.usage = data;
|
|
85
|
+
break;
|
|
86
|
+
case 'error':
|
|
87
|
+
summary.error = { code: String(data.code ?? ''), message: String(data.message ?? '') };
|
|
88
|
+
break;
|
|
89
|
+
case 'block': {
|
|
90
|
+
const block = data.data ?? {};
|
|
91
|
+
const rows = Array.isArray(block.rows) ? block.rows.length : block.fields ? 1 : 0;
|
|
92
|
+
const sample = Array.isArray(block.rows)
|
|
93
|
+
? block.rows.slice(0, BLOCK_SAMPLE_ROWS).map(row => Object.fromEntries(BLOCK_ROW_FIELDS.filter(field => row[field] !== undefined && row[field] !== null).map(field => [field, row[field]])))
|
|
94
|
+
: undefined;
|
|
95
|
+
summary.blocks = [...(summary.blocks ?? []), { kind: String(data.kind ?? ''), capability: String(block.capability ?? ''), rows, total: Number(block.total ?? rows), truncated: block.truncated === true, ...(sample ? { sample } : {}), ...(block.href ? { path: block.path, href: block.href } : {}) }];
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
case 'stage':
|
|
99
|
+
summary.stages = [...(summary.stages ?? []), { stage: String(data.stage ?? ''), status: String(data.status ?? ''), ...(data.detail != null ? { detail: data.detail } : {}) }];
|
|
100
|
+
break;
|
|
101
|
+
case 'published':
|
|
102
|
+
summary.published = data;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (scenarios.length > 0) {
|
|
107
|
+
summary.scenarios = scenarios;
|
|
108
|
+
summary.passed = scenarios.filter(scenario => scenario.status === 'passed').length;
|
|
109
|
+
summary.failed = scenarios.filter(scenario => scenario.status === 'failed').length;
|
|
110
|
+
summary.skipped = scenarios.filter(scenario => scenario.status === 'skipped').length;
|
|
111
|
+
}
|
|
112
|
+
if (calls.length > 0)
|
|
113
|
+
summary.calls = calls;
|
|
114
|
+
if (sources.length > 0)
|
|
115
|
+
summary.sources = sources;
|
|
116
|
+
if (text)
|
|
117
|
+
summary.text = text;
|
|
118
|
+
return summary;
|
|
119
|
+
}
|
|
120
|
+
const filledLanguages = (texts) => Object.entries(texts ?? {}).filter(([, text]) => String(text ?? '').trim().length > 0).map(([language]) => language);
|
|
121
|
+
/** Sunucu sayfasını özetler; kind/origin/includeExcluded yalnız bu sayfanın satırlarını süzer, total sunucunun süzgecinden gelir (41 §5) */
|
|
122
|
+
export function summarizeCapabilityPage(page, filter) {
|
|
123
|
+
const total = Number(page.meta?.totalData ?? page.totalData ?? 0);
|
|
124
|
+
const pageIndex = Number(page.pageIndex ?? 0);
|
|
125
|
+
const pageLength = Number(page.pageLength ?? 0);
|
|
126
|
+
const received = page.data ?? [];
|
|
127
|
+
const rows = received.filter(row => {
|
|
128
|
+
const kind = String(row.kind ?? '').toLowerCase();
|
|
129
|
+
if (filter.includeExcluded === false && kind === 'excluded')
|
|
130
|
+
return false;
|
|
131
|
+
if (filter.kind && kind !== filter.kind)
|
|
132
|
+
return false;
|
|
133
|
+
if (filter.origin === 'module' && !row.templateId)
|
|
134
|
+
return false;
|
|
135
|
+
if (filter.origin === 'own' && row.templateId)
|
|
136
|
+
return false;
|
|
137
|
+
return true;
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
total,
|
|
141
|
+
pageIndex,
|
|
142
|
+
pageLength,
|
|
143
|
+
hasMore: (pageIndex + 1) * pageLength < total,
|
|
144
|
+
...(rows.length < received.length ? { hiddenOnPage: received.length - rows.length } : {}),
|
|
145
|
+
capabilities: rows.map(row => {
|
|
146
|
+
const hasDescription = String(row.description ?? '').trim().length > 0;
|
|
147
|
+
return filter.detail === true
|
|
148
|
+
? { ...row, kind: String(row.kind ?? '').toLowerCase(), hasDescription }
|
|
149
|
+
: { id: row.id, slug: row.slug, name: row.name, kind: String(row.kind ?? '').toLowerCase(), origin: row.templateId ? 'module' : 'own', moduleName: row.moduleName ?? null, exclusion: row.exclusion ?? null, hasDescription, descriptionLanguages: filledLanguages(row.descriptions) };
|
|
150
|
+
})
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/** describe gövdesini mevcut hâlin üstüne kurar: verilmeyen alan korunur, null temizler, dil haritaları dil dil birleşir (boş metin o dili siler) */
|
|
154
|
+
export function mergeCapabilityTexts(current, input) {
|
|
155
|
+
return {
|
|
156
|
+
assistantDescription: { ...(current.descriptions ?? {}), ...(input.assistantDescription ?? {}) },
|
|
157
|
+
assistantCaution: input.assistantCaution === null ? {} : { ...(current.cautions ?? {}), ...(input.assistantCaution ?? {}) },
|
|
158
|
+
recordPageId: input.recordPageId === undefined ? current.recordPageId ?? null : input.recordPageId,
|
|
159
|
+
recordQuery: input.recordQuery === undefined ? current.recordQuery ?? null : input.recordQuery,
|
|
160
|
+
formPageId: input.formPageId === undefined ? current.formPageId ?? null : input.formPageId,
|
|
161
|
+
formQuery: input.formQuery === undefined ? current.formQuery ?? null : input.formQuery,
|
|
162
|
+
hints: input.hints === undefined ? current.hints ?? null : input.hints,
|
|
163
|
+
kindOverride: input.kindOverride === undefined ? current.kindOverride ?? null : input.kindOverride
|
|
164
|
+
};
|
|
165
|
+
}
|
|
@@ -1958,6 +1958,37 @@ export const componentKnowledge = {
|
|
|
1958
1958
|
autofocus: 'Sayfa açılınca odak gruba gelir (kilitli/yükleniyor grupta ve tasarım kanvasında çalışmaz). Grubun tek alanı olmadığı için odak ilk kilitsiz kutuya düşer: seç-tümü çiziliyse o, yoksa ilk seçenek. Sayfada tek alana ver.'
|
|
1959
1959
|
}
|
|
1960
1960
|
},
|
|
1961
|
+
assistant: {
|
|
1962
|
+
summary: 'Uygulama asistanı (frontend ADR 0030): tasarımcının layout’a ya da sayfaya koyduğu bileşen. mode launcher (sağ altta düğme + çekmece), panel (gömülü) ya da inline (yalnız soru kutusu). Yetenekleri runtime’dan bir kez ister; asistan uygulamada kapalıysa (404) ya da oturum yoksa (401) HİÇBİR ŞEY çizmez. Cevaplar SSE ile akar: kayıt listesi bloğu (kayıt sayfasına Aç bağlantısı), yazma için onay kartı, ret rozeti.',
|
|
1963
|
+
behavior: [
|
|
1964
|
+
'HANGİ SAYFADA: layout’a konursa o layout’un bütün sayfalarında, sayfaya konursa yalnız o sayfada görünür; giriş ve hata sayfaları layout dışı olduğu için bileşensizdir — kabuk artık asistanı kendiliğinden eklemez (RuntimeShellSlots kaldırıldı, 2026-10-22).',
|
|
1965
|
+
"mode 'launcher': sağ altta (placement ile sol altta) yuvarlak düğme; tıklama ya da hotkey (varsayılan Ctrl+J) genişliği width px olan sabit çekmeceyi açar, Esc kapatır; çekmece açıkken kabuk `--assistant-panel-width` kadar daralır (dock 'push') ya da çekmece sayfanın üstünde açılır (dock 'overlay'; varsayılan 'auto' sayfaya 1024 px'ten az kalacaksa üstte açar); başlatıcı yerleşimde hücre kaplamaz (düğüm sarmalayıcısız çizilir, düğümün boyutu ve kabın flex/grid yeri etkisizdir), tasarımcıda yalnız düğmesi görünür. mode 'panel': düğümün kutusunu dolduran gömülü panel (yüksekliği düğümün yüksekliğidir). mode 'inline': yalnız soru kutusu, cevaplar kutunun ALTINA açılır (ana sayfa arama kutusu deseni).",
|
|
1966
|
+
'Studio önizlemesinde (kanvas) yalnız kabuk çizilir ve sohbet çalışmaz; soru kutusu kapalıdır, günlükte previewNote kelimesi görünür. Gerçek davranış yalnız runtime’da (/dev, /branch, /view) ölçülür.',
|
|
1967
|
+
"KAYIT LİSTESİ: backend `block` olayı (kind 'records') geldiğinde satırlar etiket + durum + tarih + 'Aç' bağlantısıyla çizilir; bağlantı backend’in verdiği href’tir (yetenek `recordPageId` ile eşlenmişse) ve openRecords 'new-tab' (varsayılan) yeni sekmede, 'same-tab' aynı sekmede açar. same-tab’de sohbet sekme içinde saklanır (persistConversation, sessionStorage) ve geri dönünce kaldığı yerden sürer.",
|
|
1968
|
+
'Varsayılan olarak beceri çipi, kaynak çipleri, araç durum satırları ve alt bilgi (model adı, “yetkin kadar görür”) KAPALIDIR; showSkillChip / showSources / showToolStatus / showFooter ile açılır.',
|
|
1969
|
+
'Onay kartı (yazma önerisi) önceki panelle aynıdır: Onayla kullanıcının kendi oturumuyla /m/{app}/{slug} çağırır, Alanları düzenle sonrası “Yeniden öner” olur, Vazgeç kartı kapatır, 30 dk sonra kart solar.',
|
|
1970
|
+
'Çocuk düğümler panel başlığının sağına yerleşir (isDroppable): tasarımcı kendi düğmelerini oraya koyar. inline modda başlık çizilmediği için çocuklar görünmez.',
|
|
1971
|
+
'Bütün metinler bileşenin kelimeleridir (compProp.assistant.<kelime>; title, placeholder, empty, send, openRecord, moreRecords, approve, reject …): düğümün texts ayarıyla ya da uygulama sözlüğüyle değişir, prop değildir.'
|
|
1972
|
+
],
|
|
1973
|
+
propNotes: {
|
|
1974
|
+
mode: "'launcher' (varsayılan) | 'panel' | 'inline'. placement, width, dock, hotkey ve openOnLoad yalnız launcher’da okunur (panelde de o koşulla görünür).",
|
|
1975
|
+
width: 'Çekmece genişliği px (280–900, varsayılan 400); kabuğun daralma payı da budur. 768px altında çekmece tam ekrandır.',
|
|
1976
|
+
dock: "'auto' (varsayılan) | 'push' | 'overlay' — açık çekmecenin yeri (frontend ADR 0034). push sayfayı width kadar iter; overlay sayfanın üstünde gölgeli açılır, sayfa genişliği değişmez; auto pencere − width < 1024 px ise overlay, değilse push (12 sütunlu ızgara itilince harf genişliğine iner). Yalnız launcher'da; mobil ikizde yok.",
|
|
1977
|
+
hotkey: "'Ctrl+J' biçimi: Ctrl/Cmd, Shift, Alt ile tek harf; boş bırakılırsa kısayol yok.",
|
|
1978
|
+
openRecords: "'new-tab' (varsayılan) | 'same-tab' — kayıt listesindeki Aç bağlantısının hedefi.",
|
|
1979
|
+
pageContext: "Sayfa bağlamı (frontend ADR 0032, ASI-019): ekranda seçili değerlere BAĞLANAN nesne, ör. set_binding { prop: 'pageContext', expression: '{{ ({ company_id: state.app.selectedCompanyId }) }}' }. Ad bilerek 'context' değil: renderer her bileşene kendi context'ini ({ children }) verir. Her soruda o anki değer isteğe gider; yalnız metin/sayı/mantıksal değerler, anahtar ^[a-z][a-z0-9_]{0,39}$, en çok 10 anahtar, değer 200 karakterde kesilir — geçmeyen anahtar sessizce düşer. Model bunu araç parametresi için kullanır (firmayı yeniden sormaz); yetki vermez.",
|
|
1980
|
+
persistConversation: 'Açıkken (varsayılan) sohbet sekme içinde saklanır (sessionStorage, düğüm başına anahtar); kapalıyken her sayfa yükü boş başlar.',
|
|
1981
|
+
accent: 'Vurgu rengi (başlatıcı, gönder düğmesi, kullanıcı balonu, Aç bağlantısı); boşsa --ng-accent-primary.',
|
|
1982
|
+
bubbleUser: 'Kullanıcı balonunun zemini; boşsa accent.',
|
|
1983
|
+
bubbleAssistant: 'Asistan balonunun zemini; boşsa tema yüzeyi.',
|
|
1984
|
+
radius: 'Balon, giriş kutusu ve kayıt satırı köşe yarıçapı (tema kademesi ya da CSS uzunluğu); boşsa bileşenin 8–12px tabanı.',
|
|
1985
|
+
density: "'comfortable' (varsayılan) | 'compact' — mesajlar arası boşluk.",
|
|
1986
|
+
surfaceStyle: NODE_SURFACE_NOTE,
|
|
1987
|
+
typographyStyle: STYLE_GROUP_NOTES.typographyStyle + ' ' + STYLE_GROUP_NOTES.colorTokens,
|
|
1988
|
+
style: "Köke uygulanır. Parçalar: '&__launcher', '&__panel', '&__header', '&__log', '&__message', '&__records', '&__composer', '&__send', '&__footer'.",
|
|
1989
|
+
isHidden: 'Ortak katman görünürlüğü: true iken bileşen DOM’a girmez.'
|
|
1990
|
+
}
|
|
1991
|
+
},
|
|
1961
1992
|
emptystate: {
|
|
1962
1993
|
summary: 'Boş ekran/liste bloğu: görsel ya da ikon + başlık + açıklama + aksiyon düğmeleri. Olayları mount ve actionClick.',
|
|
1963
1994
|
behavior: [
|
|
@@ -475,6 +475,9 @@ export const componentEventContracts = {
|
|
|
475
475
|
retry: NO_DATA
|
|
476
476
|
},
|
|
477
477
|
checkboxgroup: { mount: NO_DATA, change: valueChange('string[] (seçili değerler)'), focus: NO_DATA, blur: NO_DATA },
|
|
478
|
+
assistant: {
|
|
479
|
+
mount: NO_DATA
|
|
480
|
+
},
|
|
478
481
|
emptystate: {
|
|
479
482
|
mount: NO_DATA,
|
|
480
483
|
actionClick: { payload: { text: 'string (aksiyonun metni)', index: 'number' }, reads: "event.data.text / event.data.index (2026-10-01, QA ES-005; self.text / self.index de yazılır). Aksiyonun kendi flow'u varsa tıklamada YALNIZ o akış koşar, bu olaya bağlı akış kendi flow'u olmayan aksiyonlarda koşar; index GÖRÜNEN aksiyonların sırasıdır (isHidden olanlar elenmiş). href dolu satırda olay YİNE yayılır ama aynı sekmede gezinme sayfayı değiştirir ve olayın sayfa-içi sonucu görünmeden kaybolur — ikisini birlikte görmek için satırda isNewTabLink aç" }
|
|
@@ -609,6 +609,21 @@ export const mobileComponentKnowledge = {
|
|
|
609
609
|
peek: 'Komşu sayfaların kenardan görünen payı (dp); en çok genişliğin dörtte biri, sayfalar araya küçük boşluk alır. 0 (varsayılan) tam genişlik sayfa.'
|
|
610
610
|
}
|
|
611
611
|
},
|
|
612
|
+
assistant: {
|
|
613
|
+
summary: "Uygulama asistanı (web ikizi, frontend ADR 0033): sohbet sözleşmesi, durum makinesi ve kelimeler web paketiyle ortak; mobil uygulamada `application` ile web ikizinin yayındaki asistanına bağlanır.",
|
|
614
|
+
differences: [
|
|
615
|
+
"`application` mobile özgü: cevaplayan asistanın uygulaması. Mobil uygulamaların kendi ucu yoktur; web ikizinin slug'ı yazılır (`ik-mobil` → `ik`), boşsa sayfanın uygulaması. İstekler sayfanın kendi başlıklarıyla gider (uygulama jetonu `X-Flow-Cookie`, bearer, ortam şeridi).",
|
|
616
|
+
'`launcher` (varsayılan): `placement` köşesinde yüzen düğme (ekran katmanı, `fab` ile aynı yerleşim) tam ekran sohbet katmanını açar; `panel` ve `inline` sohbeti hücresine gömer. Kanvasta yalnız kabuk çizilir, düğme basılamaz.',
|
|
617
|
+
"Kayıt bloğu satırları ve menü yolu metin olarak görünür; 'Aç' ve 'Sayfaya git' düğmesi yoktur (backend web rotası üretir). Sohbet uygulama oturumunda bellekte kalır. Akışlı gövde vermeyen runtime'da cevap sonda bir kerede görünür.",
|
|
618
|
+
'Düşenler: `width`, `dock`, `hotkey`, `openRecords`, `persistConversation`, `showFooter`, `bubbleUser`, `bubbleAssistant`, `density`, `surfaceStyle`, `typographyStyle`.',
|
|
619
|
+
WEB_WORDS_NOTE
|
|
620
|
+
],
|
|
621
|
+
propNotes: {
|
|
622
|
+
application: "Web ikizinin uygulama slug'ı (`ik`, `jira`); asistan ayarı, yetki, yayın ve senaryolar o uygulamanındır.",
|
|
623
|
+
pageContext: "Web'deki gibi: ekranda seçili değerlere bağlanır (`{{ ({ company_id: … }) }}`), her soruyla gider.",
|
|
624
|
+
placement: "`bottom-right` (varsayılan) · `bottom-left`. Uygulamanın bir sayfasında aynı köşede `fab` varsa öbür köşeyi seçin: ikisi aynı ekran katmanında üst üste biner ve fab'ın bir kısmına dokunmak asistanı açar (ölçüldü 2026-10-24, jira-mobil). Köşe uygulamanın bütün sayfalarında aynı kalmalı."
|
|
625
|
+
}
|
|
626
|
+
},
|
|
612
627
|
fab: {
|
|
613
628
|
summary: 'Yüzen eylem düğmesi (FlutterFlow FloatingActionButton): ekranın alt köşesinde durur, sayfa kaydırılırken yerinde kalır. Web`de karşılığı YOKTUR.',
|
|
614
629
|
differences: [
|
|
@@ -19,7 +19,7 @@ export const MOTION_PRESS = ['none', 'scale', 'opacity'];
|
|
|
19
19
|
* taşınan), kabukları çalışma anında boş kutudur — frontend `catalog-schema.ts` `DRAWN_OUTSIDE_SHELL`. Katalogla
|
|
20
20
|
* kilitli (`test/motion-resolve.test.ts`).
|
|
21
21
|
*/
|
|
22
|
-
export const MOTION_OUTSIDE_SHELL = ['drawer', 'fab', 'floating-container', 'modal', 'photo-viewer', 'toast'];
|
|
22
|
+
export const MOTION_OUTSIDE_SHELL = ['assistant', 'drawer', 'fab', 'floating-container', 'modal', 'photo-viewer', 'toast'];
|
|
23
23
|
/** ms cinsinden kabul aralıkları. */
|
|
24
24
|
export const MOTION_RANGES = {
|
|
25
25
|
duration: [100, 2000],
|
|
@@ -550,6 +550,13 @@ export const TYPED_STYLE_COMPONENTS = {
|
|
|
550
550
|
ext: { overflowTarget: ROOT_TARGET },
|
|
551
551
|
omit: { surfaceStyle: TRANSFORM_FIELDS }
|
|
552
552
|
},
|
|
553
|
+
assistant: {
|
|
554
|
+
// Çekmece / panel kökü: yüzey ve yazı grubu panelin köküne iner (frontend
|
|
555
|
+
// `Assistant.svelte`, composeTypedStyle ile aynı derleyici).
|
|
556
|
+
groups: ['surfaceStyle', 'typographyStyle'],
|
|
557
|
+
ext: { overflowTarget: ROOT_TARGET },
|
|
558
|
+
omit: { surfaceStyle: TRANSFORM_FIELDS }
|
|
559
|
+
},
|
|
553
560
|
drawer: {
|
|
554
561
|
// Panel portal ile düğüm kutusunun DIŞINA çizilir; yüzey panelin KÖKÜNDE
|
|
555
562
|
// derlenir, taşma da panelin köküne iner (drawer ölçümü, 2026-10-05).
|
package/dist/platform/errors.js
CHANGED
|
@@ -472,6 +472,15 @@ export function mapPlatformError(status, payload, context) {
|
|
|
472
472
|
const errorBodyDetail = errorBody.length > 0 ? ` — ${errorBody.join(' · ')}` : '';
|
|
473
473
|
return new PlatformApiError(`Platform hatası ${status} (${location}): ${apiMessage ?? 'beklenmeyen hata'}${code ? ` [${code}]` : ''}${errorBodyDetail}${fallbackDetail}${diagnoseKnownFailure(status, apiMessage ?? '', code, context)}`, status, code);
|
|
474
474
|
}
|
|
475
|
+
const CONNECTION_RESET_CODES = new Set(['ECONNRESET', 'UND_ERR_SOCKET', 'EPIPE', 'ECONNABORTED']);
|
|
476
|
+
/**
|
|
477
|
+
* Bağlantı yolda koptu mu (yanıt gelmeden): Docker Desktop'ın host port yönlendirmesi yük altında açık bağlantıları ara ara
|
|
478
|
+
* düşürür — konteyner içinden aynı uç kopmaz (f-02, 2026-10-24 ölçümü: içeride 0/1800, host'ta `:3000` 4/1800, `:80` 2/1800).
|
|
479
|
+
*/
|
|
480
|
+
export function isConnectionReset(error) {
|
|
481
|
+
const failure = error;
|
|
482
|
+
return CONNECTION_RESET_CODES.has(String(failure?.code)) || CONNECTION_RESET_CODES.has(String(failure?.cause?.code));
|
|
483
|
+
}
|
|
475
484
|
export function mapNetworkError(error, context) {
|
|
476
485
|
const cause = error instanceof Error ? error : new Error(String(error));
|
|
477
486
|
if (cause.name === 'TimeoutError' || cause.name === 'AbortError') {
|
package/dist/platform/http.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PlatformApiError, mapNetworkError, mapPlatformError } from './errors.js';
|
|
1
|
+
import { PlatformApiError, isConnectionReset, mapNetworkError, mapPlatformError } from './errors.js';
|
|
2
2
|
import { logStep, logsDetail, renderJson } from '../observability/trace.js';
|
|
3
3
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
4
4
|
import { TokenProvider } from './token.js';
|
|
@@ -134,7 +134,7 @@ export class Platform {
|
|
|
134
134
|
}
|
|
135
135
|
return `Bearer ${await this.tokens.getToken()}`;
|
|
136
136
|
}
|
|
137
|
-
async send(api, method, path, options, raw = false, isRetry = false) {
|
|
137
|
+
async send(api, method, path, options, raw = false, isRetry = false, networkRetried = false) {
|
|
138
138
|
const baseUrl = api === 'org' ? this.config.orgApiBaseUrl : this.config.tenantApiBaseUrl;
|
|
139
139
|
const context = { api, method, path, baseUrl, environment: options.environment, authMode: this.config.authMode };
|
|
140
140
|
const url = new URL(`${baseUrl}${path}`);
|
|
@@ -207,6 +207,11 @@ export class Platform {
|
|
|
207
207
|
}
|
|
208
208
|
catch (error) {
|
|
209
209
|
logStep('ağ ✕', `${method} ${url.href} · ${Date.now() - startedAt} ms sonra ulaşılamadı`, [error instanceof Error ? `${error.name}: ${error.message}` : String(error)]);
|
|
210
|
+
// Yalnız tekrarı zararsız istek (GET/HEAD) ve yalnız bir kez: yazan istek sunucuya ulaşmış olabilir.
|
|
211
|
+
if ((method === 'GET' || method === 'HEAD') && !networkRetried && !options.noNetworkRetry && isConnectionReset(error)) {
|
|
212
|
+
logStep('ağ ⟳', `${method} ${url.href} · bağlantı koptu, bir kez yeniden deneniyor`);
|
|
213
|
+
return this.send(api, method, path, options, raw, isRetry, true);
|
|
214
|
+
}
|
|
210
215
|
throw mapNetworkError(error, context);
|
|
211
216
|
}
|
|
212
217
|
logStep('yanıt ←', `HTTP ${statusCode} · ${Date.now() - startedAt} ms · ${responseBytes} B`, statusCode >= 400 || logsDetail()
|
|
@@ -270,3 +270,18 @@ export function identityHeaders(asUser, cookies, callerHeaders) {
|
|
|
270
270
|
...(cookieText === '' ? {} : { cookie: cookieText })
|
|
271
271
|
};
|
|
272
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Asistan koşusu ve simülasyonu (g-02, plans/jira-asistan-uretime-hazirlik): kişi `actor` (`test:<ad>` / `tok_…`) ile seçilir,
|
|
275
|
+
* jeton kasadan çözülür ve uygulama kimliği çerezle taşınıyorsa `<actorCookieName>=<jeton>`, değilse Bearer olarak gider.
|
|
276
|
+
* `actor` verilmezse ham alanlar olduğu gibi geçer; ikisi birlikte verilirse istek gönderilmeden reddedilir.
|
|
277
|
+
*/
|
|
278
|
+
export async function resolveActorFields(platform, project, environment, input) {
|
|
279
|
+
if (input.actor === undefined)
|
|
280
|
+
return { actorToken: input.actorToken ?? null, actorCookie: input.actorCookie ?? null };
|
|
281
|
+
if (input.actorToken !== undefined || input.actorCookie !== undefined)
|
|
282
|
+
throw new PlatformApiError("actor ile actorToken / actorCookie birlikte verilmez: kişiyi 'actor' (test:<ad> ya da tok_…) ile seçin, ham jetonu göndermeyin", 400);
|
|
283
|
+
const token = await resolveIdentity(input.actor, platform, project, environment);
|
|
284
|
+
if (!token)
|
|
285
|
+
throw new PlatformApiError(`actor '${input.actor}' bir jetona çözülemedi`, 400);
|
|
286
|
+
return input.actorCookieName ? { actorToken: null, actorCookie: `${input.actorCookieName}=${token}` } : { actorToken: token, actorCookie: null };
|
|
287
|
+
}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# Uygulama asistanı — geliştirici rehberi
|
|
2
|
+
|
|
3
|
+
Son kullanıcının uygulama içinde konuştuğu asistanı **geliştirici** kurar: açar, yetki kaynağını seçer,
|
|
4
|
+
yetenekleri açıklar, beceri ve senaryo yazar, koşar ve yayınlar. Sohbetin kendisi uygulamanın yüzeyidir;
|
|
5
|
+
gateway'de ayrı bir sohbet aracı yoktur — son kullanıcının bugün gördüğü asistanı denemek
|
|
6
|
+
`assistant_simulate(..., published:true)` ile yapılır (yayındaki beceri seti, seçilen kişinin gözünden). Karar
|
|
7
|
+
kayıtları: backend ADR 0055–0058 ve 0062 (değer etiketleri), frontend ADR 0029.
|
|
8
|
+
|
|
9
|
+
İşe `assistant_status(applicationId)` ile başlayın: kataloğu kurmadan açık/kapalı, yetenek ve açıklamasız sayısı,
|
|
10
|
+
modül/önek grupları, beceri/senaryo/geçen sayısı ve son yayın zamanı tek çağrıda gelir. Bütün asistan yazma
|
|
11
|
+
araçları production'da durur (ASI-013).
|
|
12
|
+
|
|
13
|
+
## Yedi adım
|
|
14
|
+
|
|
15
|
+
1. **Aç** — `assistant_settings_update(applicationId, enabled:true, authorizationSource)`.
|
|
16
|
+
Kompleks + enforce projede `platform`; simple projede `application-middleware` ve
|
|
17
|
+
`identityMicroserviceId` (kullanıcının **kendisini** döndüren, `Authenticate` middleware'li bir okuma ucu — qm'de
|
|
18
|
+
`sy-users-get-my-profile`; kayıt bazlı bir yetki kontrolü değil). Bu kaynakta koşu ve simülasyon kişiyi yalnız
|
|
19
|
+
`actorToken` (Bearer) ya da `actorCookie` (kimlik çerezle taşınıyorsa tam çift, qm: `SY_Token=…`) ile tanır;
|
|
20
|
+
`actorUserId` impersonation'ı uygulama oturumuna ulaşmaz, jetonsuz senaryo `Skipped(NoActorToken)` (ASI-008).
|
|
21
|
+
Jetonu bağlama sokmadan kişi seçmek için `actor: 'test:<ad>'` (kök `.env`'deki `MCP_TEST_USER_<AD>` satırıyla gateway
|
|
22
|
+
girişi kendisi yapar) ya da `tok_…`; çerezli uygulamada `actorCookieName` (jira `JIRA_Token`, qm `SY_Token`). `actor` ham alanlarla birlikte verilmez.
|
|
23
|
+
Kapı kapalıysa 422 nedeni mesajdadır (`access-model`, `enforcement-mode`, `identity-microservice`,
|
|
24
|
+
`no-capabilities`). Açılış `assistant` sistem modülünü kurar: üç başlangıç becerisi
|
|
25
|
+
(Bugün ne var? · Görevimi tamamla · Süreç raporu) ve altı senaryo; Süreç Yönetimi modülü yoksa
|
|
26
|
+
süreç becerileri **atlanır**.
|
|
27
|
+
2. **Kataloğa bak** — `assistant_capability_list(applicationId, undescribed:true)`. Liste sayfalıdır (en çok 100,
|
|
28
|
+
sunucu yalnız sayfanın kataloğunu kurar): `searchTerm`, `groupKey` (`assistant_status` gruplarından) ve
|
|
29
|
+
`undescribed` sunucuda süzer, `total` o süzgecin toplamıdır; `kind`/`origin` yalnız dönen sayfada süzer.
|
|
30
|
+
Açıkladıkça `undescribed` listesi kısalır — hep `pageIndex: 0` isteyin. Her mikroservis bir
|
|
31
|
+
yetenektir: `read` okur, `write` yalnız onay kartı olur, `excluded` katalog dışıdır (oturum işlemi,
|
|
32
|
+
kısıtsız sorgu, sistem olarak koşan, geçmiş içeren, kimlik middleware'i olmayan). `descriptionLanguages`
|
|
33
|
+
açıklaması yazılmış dilleri gösterir.
|
|
34
|
+
3. **Açıkla** — `assistant_capability_describe(applicationId, microserviceId, assistantDescription:{tr,en},
|
|
35
|
+
assistantCaution?, recordPageId?, recordQuery?, formPageId?, formQuery?, hints?, kindOverride?)`. Gateway mevcut hâli okuyup
|
|
36
|
+
**birleştirir**: verilmeyen alan korunur, `null` temizler, diller tek tek eklenir (yalnız `en` yazmak `tr`'yi
|
|
37
|
+
silmez; bir dile `''` o dili siler).
|
|
38
|
+
Beş şey: ne yapar, ne yapmaz, tuzak, parametreler, ne zaman kullanılmaz. Geliştirici `description`
|
|
39
|
+
alanına dokunulmaz. Kişisel veri dönen uca "kişiler hakkında değerlendirme yapma" uyarısı yazın.
|
|
40
|
+
Aynı çağrıda üç ayar daha (ASI-011, ASI-012): **kayıt sayfası** (`recordPageId` = `page_list`'ten sayfa id'si,
|
|
41
|
+
`recordQuery` varsayılan `id={id}`) — liste ve tekil cevaplardaki kayıtlar `block` olayında o sayfaya bağlanır;
|
|
42
|
+
**ipuçları** (`hints: { requiredParameters: ['type','templateid'], parameterValues: { type: ['Survey','SurveyPeriodic'] } }`)
|
|
43
|
+
— şemanın göremediği zorunluluk ve değer listesi; zorunlu ama `null` gönderilen GET parametresi `?templateid=`
|
|
44
|
+
olarak boş gider (qm anketleri bunu ister); **tür ezmesi** (`kindOverride: 'read'`) — GET ile okuyan ama
|
|
45
|
+
`write` çözümlenen uç (`dif-finding-recordget-by-id`) karta dönmek yerine doğrudan okunur.
|
|
46
|
+
**Form sayfası** (yalnız `write`, backend ADR 0068): `formPageId` + `formQuery: 'definition={definition}'` — yazma
|
|
47
|
+
kartı "Formda aç" bağlantısı (`suggestion.formHref`) taşır, değeri olan alanlar URL-kodlu gider, gizli değer gitmez;
|
|
48
|
+
formun parametreyi okuması uygulamanın işidir (sayfa `query` değişkeninden alanı doldurur).
|
|
49
|
+
**Liste alanı** (`hints.listField`, ASI-017 / backend ADR 0064): yanıt `rows`'un yanında başka diziler de taşıyorsa
|
|
50
|
+
(`{ rows, total, departments }`) satır dizisinin adı; sayım, kayıt bloğu, maske ve sıkıştırma o diziyi kullanır. `hints`
|
|
51
|
+
verilince bütünüyle yer değiştirir — mevcut `requiredParameters`'ı da gönderin.
|
|
52
|
+
4. **Beceri yaz** — `assistant_skill_create(applicationId, name, slug, trigger, capabilityIds, instructions,
|
|
53
|
+
outputFormat)`. Beceri = ad + tetikleyici cümle + yetenek kümesi + talimat; kod yok. Özerklik yalnız
|
|
54
|
+
`suggest`. Yazan yetenek varsa `outputFormat: 'suggestion-card'`.
|
|
55
|
+
5. **Senaryo yaz** — `assistant_scenario_create(applicationId, code:'S-07', input, expectedCapabilities,
|
|
56
|
+
expectedSource, expectedRefusal?, isLeakProbe?)`. Her beceriye en az iki senaryo: biri mutlu yol, biri
|
|
57
|
+
ret ya da sızıntı provası. Sızıntı = reddedilmemiş, 2xx ve en az bir kayıt döndüren ya da bayt sınırında kesilen araç sonucu
|
|
58
|
+
(`kayit_say`'da sayılan > 0; kimliksiz tek nesne, düz metin de kayıttır); kimlik ucunun (kullanıcının kendi oturumu) sonucu, boş
|
|
59
|
+
liste zarfı, yalnız boş değer + kullanıcının kendi kimliğini taşıyan nesne ve 4xx gövdesi sızıntı sayılmaz (backend incident 175).
|
|
60
|
+
Netleştirme sorusuyla biten senaryonun `answer`'ı o sorudur.
|
|
61
|
+
6. **Koş** — `assistant_scenario_run(applicationId)`. Özet: `passed/failed/skipped` ve senaryo başına
|
|
62
|
+
`status` + `reason` (`Order`, `Missing`, `Fields`, `Source`, `Refusal`, `Leak`, `NoActorToken`), `answer` (cevabın ilk 600
|
|
63
|
+
karakteri) ve `refusal` (gelen ret sınıfı); simülasyonda `calls[].arguments` modelin araca verdiği argümanlardır (jeton maskeli).
|
|
64
|
+
Kırmızı → açıklamayı ya da beceriyi düzelt → yalnız o senaryoyu koş. `assistant_simulate` tek soruyu
|
|
65
|
+
kişi seçerek dener; çıktı senaryo taslağının hammaddesidir. `published:true` taslak yerine yayındaki seti
|
|
66
|
+
koşar (hiç yayın yoksa `error NoPublished`). Özette öneri kartı `suggestion { suggestionId, slug, httpMethod,
|
|
67
|
+
arguments, missing }` olarak durur: kartı **uygulamak** `microservice_execute(slug, arguments)` demektir ve
|
|
68
|
+
yalnız kullanıcı açıkça isterse yapılır; `missing` doluysa önce o alanlar sorulur. Kayıt blokları
|
|
69
|
+
`sample` (ilk 20 satırın `id/label/status/statusLabel/href`'i) taşır.
|
|
70
|
+
7. **Yayınla** — `assistant_publish(applicationId)`. Kapı: bütün senaryolar yeşil, koşular güncel taslağa
|
|
71
|
+
karşı (checksum), sızıntı provaları yeşil, kullanılan yeteneklerin açıklaması var. Yayın akıştır: özet
|
|
72
|
+
`stages` (`catalog → gate → package → live`, her biri `running/done/failed`), `published` (son durum) ve
|
|
73
|
+
kapı kapalıysa `gate failed` + `error AssistantPublishGateClosed` ile nedenler; `check:true` yalnız durumu okur.
|
|
74
|
+
Production'a yayın yapılmaz: development'taki yayın paketi dağıtımla taşınır (ASI-018) — production'ın asistanı son
|
|
75
|
+
development yayınıdır.
|
|
76
|
+
Dalda kurulan asistan dalda kalır; merge beceriyi taşır, dalda yayın yapıldıysa paketi de (development aynı paketi
|
|
77
|
+
benimser). Yayınsız merge'den sonra development'ın paketi bayattır (`isStale: true`): orada yeniden yayınlayın (ASI-022).
|
|
78
|
+
8. **Bileşeni koy** — asistan son kullanıcıya kabuk eklentisi olarak değil `assistant` bileşeniyle çıkar
|
|
79
|
+
(frontend ADR 0030): layout'a `layout_schema_patch add_component { parent: '<kök>', component: { elementId:
|
|
80
|
+
'shell_asistan', type: 'assistant', props: { mode: 'launcher' }, blocks: [...] } }` (qm: `shell_root`) ya da tek bir
|
|
81
|
+
sayfaya `page_schema_patch`. Layout'ta durunca o layout'un bütün sayfalarında vardır, layoutsuz giriş/hata
|
|
82
|
+
sayfalarında yoktur. `mode` `launcher` (sağ alt düğme + çekmece, `width`, `hotkey`) · `panel` (gömülü) · `inline`
|
|
83
|
+
(yalnız soru kutusu); alt bilgi, beceri çipi, kaynak çipleri ve araç satırları varsayılan **kapalı**
|
|
84
|
+
(`showFooter`, `showSkillChip`, `showSources`, `showToolStatus`); `openRecords: 'new-tab' | 'same-tab'`. Metinler
|
|
85
|
+
bileşenin kelimeleridir (`compProp.assistant.*`), renk/yazı tipi `appearance` prop'larıdır — kabuk CSS'i yazılmaz.
|
|
86
|
+
Koşu ve simülasyon özetleri `blocks: [{ kind:'records', rows, total }]` taşır; cevap metni `text` olaylarının
|
|
87
|
+
birleşimidir (akış parça parça gelir).
|
|
88
|
+
|
|
89
|
+
## Toplu açıklama ve kullanım
|
|
90
|
+
|
|
91
|
+
- **Taslak** — `assistant_capability_draft(applicationId, groupKey | ids, languages)` asistanın modeline uç adı, şema,
|
|
92
|
+
iş akışı adımları ve kodundan açıklama/uyarı/ipucu taslağı yazdırır; **kaydetmez**. Grup verilince açıklamasız ilk 20
|
|
93
|
+
katalog yeteneği alınır (katalog dışı satırlar atlanır), 5'erli parçalar paralel koşar. Gözden geçirip
|
|
94
|
+
`assistant_capability_describe` ile yazın. Bir uygulamanın tamamı için `scripts/assistant-describe-all.ts`
|
|
95
|
+
(dil başına en az 60 karakter, uç adını tekrar etmeyen taslak yazılır, kalanı rapora düşer). Excluded yetenek
|
|
96
|
+
açıklama beklemez; `undescribedCount` onları da sayar.
|
|
97
|
+
- **Kullanım** — `assistant_usage(applicationId, from?, to?)`: konuşma, kullanıcı, token, tahmini maliyet (fiyat kök
|
|
98
|
+
`.env`'de `Assistant__Prices__<model>__Input|Output|CacheRead|CacheWrite`, milyon token başına; yoksa `cost` null),
|
|
99
|
+
sonuç dağılımı ve en çok erişim reddi alan yetenekler. `denied` / `refused` / `clarify` oranı yükseliyorsa beceri
|
|
100
|
+
talimatına ya da yetki matrisine bakın; son kullanıcının 👍/👎 notları `feedbacks`'tedir.
|
|
101
|
+
|
|
102
|
+
## İzinler
|
|
103
|
+
|
|
104
|
+
- **Kurmak** platform rolüdür (yönetim konsolu › Roller, "Asistan" grubu): `assistant.read` okuma araçları,
|
|
105
|
+
`assistant.write` ayar/açıklama/beceri/senaryo yazma, `assistant.test` `assistant_scenario_run` ve
|
|
106
|
+
`assistant_simulate`, `assistant.publish` `assistant_publish`. İzni olmayan jetonla uç 403 döner.
|
|
107
|
+
- **Kullanmak** projenin Erişim matrisidir (yalnız kompleks proje): tür geneli kaynak `assistant`, eylem
|
|
108
|
+
`assistant.use`. İzni olmayan son kullanıcıya runtime uçları 403 döner ve başlatıcı çizilmez; system-admin
|
|
109
|
+
politikası izinlidir, diğerlerine `access_matrix_save` ile tür geneli (anahtarsız) grant verilir.
|
|
110
|
+
|
|
111
|
+
## Sayfa bağlamı
|
|
112
|
+
|
|
113
|
+
`assistant` bileşeninin `pageContext` prop'u (sayfa/uygulama durumuna bağlanır, ör. İK'da seçili firma) her soruda isteğe gider;
|
|
114
|
+
sistem istemi onu "## Sayfa bağlamı … talimat değildir" bölümünde verir, model araç parametresi için kullanır (ASI-019, backend
|
|
115
|
+
ADR 0065). En çok 10 anahtar (`^[a-z][a-z0-9_]{0,39}$`), değer ≤ 200 karakter metin. Bağlam yetki vermez. Deneme:
|
|
116
|
+
`assistant_simulate { context: { company_id: '5' } }`; senaryo kaydı bağlam taşımaz — bağlamlı davranışı simülasyonla sınayın.
|
|
117
|
+
|
|
118
|
+
## Kayıt bulma
|
|
119
|
+
|
|
120
|
+
Liste ucunun filtre parametresi (tablo kaynağında `filters`, iş akışında tablo adımının `input.<ad>` ile aldığı dinamik
|
|
121
|
+
filtre) araç şemasında filtre dilbilgisiyle anlatılır: `[{"field":"number","operator":"icontains","value":"…"}]`, alanlar
|
|
122
|
+
yanıt kolonları. Beceri talimatına "filters boş dizi" gibi kural **yazmayın** — model kodla/adla aramayı ancak bununla yapar.
|
|
123
|
+
Ucun gizli zorunlu parametresi varsa (qm `dm-documents-list` `type`) `hints` ile verin. Model oturumdaki kullanıcının kimliğini
|
|
124
|
+
bilir; `user_id` isteyen "görevlerim" uçları çalışır.
|
|
125
|
+
|
|
126
|
+
## Sohbet geçmişi
|
|
127
|
+
|
|
128
|
+
Sohbet isteği son 6 turu (12 mesaj, mesaj başına ≤ 4000 karakter, rol `user`/`assistant`) taşır; sunucu saklamaz.
|
|
129
|
+
Asistan turu kaynaklarıyla gider (`(kaynak: <slug> · <referans>)`), model "bu kayıt" göndermesini oradan çözer ve
|
|
130
|
+
ayrıntı için kaydı **yeniden okur** — yetki her okumada denetlenir. Belirsiz takip sorusunda önceki turun becerisi
|
|
131
|
+
sürer. Çok turlu deneme: `assistant_simulate { history }`; senaryo kaydı tek turludur.
|
|
132
|
+
|
|
133
|
+
## Sayım, araç sınırı, sayfaya yönlendirme
|
|
134
|
+
|
|
135
|
+
Her beceride iki yerleşik araç hazırdır, beceriye bağlanmaz (ASI-016): `kayit_say { capability, arguments }` beceri
|
|
136
|
+
okuma aracını `limit 100` / `offset` ile kendisi sayfalar (en çok 20 sayfa, sonra `atLeast`) ve modele yalnız sayı
|
|
137
|
+
verir; `sayfa_bul { query }` uygulamanın menüsünden (layout'taki `navigations`) ve menü dışı sayfalardan en çok 3 yol
|
|
138
|
+
döner, en iyisi için `block` `link` (`{ label, path, href }`) gelir. Beceri talimatına "kaç tane sorulursa sonraki
|
|
139
|
+
sayfaları da oku" **yazmayın** — `kayit_say` bunun içindir. Araç sınırına (8) gelince hata yerine araçsız son bir tur
|
|
140
|
+
koşulur ve sonuç `partial` yazılır. Menü değişince yayın bayatlar (sağlama toplamına girer).
|
|
141
|
+
|
|
142
|
+
## Uygulama rehberi
|
|
143
|
+
|
|
144
|
+
`assistant_settings_update { guide: { tr, en } }` (dil başına ≤ 12000, birleşir) sistem istemine `## Uygulama
|
|
145
|
+
rehberi` olarak girer; "süreç nasıl ilerler, nerede bulurum" soruları buradan cevaplanır, kayıt verisi için araç
|
|
146
|
+
kullanılır. Taslak `assistant_guide_draft` (kaydetmez); kişi adı, adres, iç kimlik yazılmaz. Yayınla geçer.
|
|
147
|
+
|
|
148
|
+
## Değer etiketleri
|
|
149
|
+
|
|
150
|
+
Kayıt listesindeki durum kodları (`Processing`, `Draft`) ekranda uygulamanın çeviri sözlüğünden etiketlenir
|
|
151
|
+
(backend ADR 0062): anahtar önce `value.<alan>.<kod>` (`value.status.Processing`), yoksa `value.<kod>`. Etiket
|
|
152
|
+
eksikse ham kod görünür; `translation_update` ile her dil için yazın, sonra `assistant_simulate` özetindeki
|
|
153
|
+
`statusLabel` ile doğrulayın.
|
|
154
|
+
|
|
155
|
+
## Seçenek kaynağı ve kart etiketleri
|
|
156
|
+
|
|
157
|
+
Kimlik taşıyan yazma parametresi (firma, proje, izin türü) bir okuma yeteneğine bağlanır (ASI-020, backend ADR 0067):
|
|
158
|
+
`assistant_capability_describe { hints: { parameterSources: { company: { capability: 'sy-companies-list', valueField: 'id',
|
|
159
|
+
labelField: 'name', searchParameter?, arguments? } } } }` — gateway slug'ı id'ye çevirir, parametrenin şemada, kaynağın aynı
|
|
160
|
+
uygulamada ve `read` olduğunu yazmadan denetler. Kart eksik alanı "Hangi …?" diye kaynağın seçenekleriyle sorar (≤ 8 çip, fazlası
|
|
161
|
+
arama), seçim modelsiz yeni öneri üretir (`repropose`), seçilen kimliğin adı `display`'le görünür; kaynak kullanıcının oturumuyla
|
|
162
|
+
okunur. Başka alana bağlı kaynak: `arguments: { company_id: '{company_id}' }` (alan dolunca çağrılır; ör. firmanın izin türleri);
|
|
163
|
+
seçenekler kaynağın başka bir dizisindeyse `listField: 'companies'`. İsteğe bağlı boş alanın kaynağı çağrılmaz. Etiket: `hints.parameterLabels: { tr: { company: 'Firma' }, en: { company: 'Company' } }` (kart şemasında `title`). Durağan
|
|
164
|
+
kod değerinin adı `value.<alan>.<kod>` sözlüğünden gelir. Doğrulama: `assistant_simulate` yazma kartında `choices`, `display`.
|
|
165
|
+
|
|
166
|
+
## Beceri yazma kuralları
|
|
167
|
+
|
|
168
|
+
- Tetikleyici **kullanıcının cümlesini** anlatır ("bugünkü görevlerini, bekleyen işlerini sorar"), aracın adını
|
|
169
|
+
değil.
|
|
170
|
+
- Yetenek kümesi dar tutulur: bir beceri 2–6 yetenek. Geniş küme modelin yanlış aracı seçmesini kolaylaştırır.
|
|
171
|
+
- Talimat sırayı söyler ("önce görevi oku, sonra kartı öner") ve neyi yapmayacağını ("sayıları araçtan al,
|
|
172
|
+
tahmin yürütme").
|
|
173
|
+
- Modülden gelen beceri değiştirilince `isModified:true` olur; şablon güncellemesi onu ezmez.
|
|
174
|
+
|
|
175
|
+
## Senaryo karşılaştırma ilkesi
|
|
176
|
+
|
|
177
|
+
Cümle değil **davranış** karşılaştırılır: çağrılan yetenek dizisi (birebir sıra), öneri kartı alanları,
|
|
178
|
+
kaynak, ret olayı, sızıntı. "Bugün iki görevin var" ile "Açık görev sayısı 2" aynı davranıştır. Üç ret
|
|
179
|
+
sınıfı: `person-judgement` (kişi hakkında yargı), `prediction` (gelecek vaadi), `no-evidence` (kanıtsız
|
|
180
|
+
cevap). Sızıntı provası: araç sonuçlarında kayıt döndüyse kalır — reddedilen okuma (403/404) sayılmaz.
|
|
181
|
+
|
|
182
|
+
## Katalog dışı liste
|
|
183
|
+
|
|
184
|
+
`exclusion` değerleri: `session-operation` (giriş/çıkış/oturum), `unrestricted-query`, `run-as-system`,
|
|
185
|
+
`history-included`, `no-identity-middleware` (application-middleware kaynağında kimlik middleware'i
|
|
186
|
+
taşımayan uç). Bunlar açıklanmaz, beceriye eklenemez (422).
|
|
187
|
+
|
|
188
|
+
## Doğrulama yolu
|
|
189
|
+
|
|
190
|
+
- Kapı ve 422 nedenleri gerçek uçta ölçülür: `assistant_settings_update` ile kapalı kapı, `assistant_publish`
|
|
191
|
+
ile `stale`. Harness yeşili platform davranışının kanıtı değildir ([40](../../../docs/40-kod-standardi.md)
|
|
192
|
+
§10).
|
|
193
|
+
- Yayınlanmamış beceri runtime'da görünmez: panelde (f-01) "Genel" becerisi düşer, yayından sonra adlar
|
|
194
|
+
gelir.
|