@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.
Files changed (48) hide show
  1. package/.env.example +5 -0
  2. package/dist/config.js +2 -0
  3. package/dist/knowledge/access.js +4 -3
  4. package/dist/knowledge/assistantRun.js +165 -0
  5. package/dist/knowledge/componentCatalog.js +31 -0
  6. package/dist/knowledge/componentEvents.js +3 -0
  7. package/dist/knowledge/mobileComponentCatalog.js +15 -0
  8. package/dist/knowledge/motionResolve.js +1 -1
  9. package/dist/knowledge/styleResolve.js +7 -0
  10. package/dist/platform/errors.js +9 -0
  11. package/dist/platform/http.js +7 -2
  12. package/dist/platform/tokenVault.js +15 -0
  13. package/dist/resources/guides/assistant.md +194 -0
  14. package/dist/resources/guides/deployment.md +19 -1
  15. package/dist/resources/schema-fingerprint.json +4 -4
  16. package/dist/resources/schemas/AccessMatrixDto.json +2 -1
  17. package/dist/resources/schemas/AccessPermissionDto.json +2 -1
  18. package/dist/resources/schemas/AccessResourceDto.json +2 -1
  19. package/dist/resources/schemas/AssistantCapabilitiesDto.json +256 -0
  20. package/dist/resources/schemas/AssistantPublishStateDto.json +57 -0
  21. package/dist/resources/schemas/AssistantScenarioDto.json +91 -0
  22. package/dist/resources/schemas/AssistantScenarioRunDto.json +62 -0
  23. package/dist/resources/schemas/AssistantSettingsDto.json +52 -0
  24. package/dist/resources/schemas/AssistantSkillDto.json +78 -0
  25. package/dist/resources/schemas/AssistantSummaryDto.json +100 -0
  26. package/dist/resources/schemas/CreateAssistantScenarioCommand.json +81 -0
  27. package/dist/resources/schemas/CreateAssistantSkillCommand.json +83 -0
  28. package/dist/resources/schemas/CreateMicroserviceCommand.json +16 -0
  29. package/dist/resources/schemas/DescribeAssistantCapabilityCommand.json +145 -0
  30. package/dist/resources/schemas/GlobalFunctionUpdateResultDto.json +5 -1
  31. package/dist/resources/schemas/MicroserviceDto.json +16 -0
  32. package/dist/resources/schemas/PublishAssistantCommand.json +15 -0
  33. package/dist/resources/schemas/RunAssistantScenariosCommand.json +39 -0
  34. package/dist/resources/schemas/SimulateAssistantCommand.json +106 -0
  35. package/dist/resources/schemas/UpdateAssistantScenarioCommand.json +79 -0
  36. package/dist/resources/schemas/UpdateAssistantSettingsCommand.json +52 -0
  37. package/dist/resources/schemas/UpdateAssistantSkillCommand.json +81 -0
  38. package/dist/resources/schemas/UpdateMicroserviceCommand.json +16 -0
  39. package/dist/server.js +2 -0
  40. package/dist/tools/applications.js +4 -4
  41. package/dist/tools/assistant.js +382 -0
  42. package/dist/tools/deployments.js +9 -3
  43. package/dist/tools/microservices.js +1 -1
  44. package/dist/tools/mobile.js +2 -2
  45. package/dist/tools/projects.js +2 -2
  46. package/dist/tools/templates.js +1 -1
  47. package/dist/tools/themes.js +2 -2
  48. 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
  };
@@ -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).
@@ -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') {
@@ -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.