@appaflytech/zeyrek-ai-mcp 0.0.59 → 0.0.61

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.
@@ -31,7 +31,8 @@ const stepKindMatrix = {
31
31
  response: ['workflow', 'middleware'],
32
32
  convert: ['workflow'],
33
33
  revert: ['automation'],
34
- balance: ['workflow', 'automation', 'middleware']
34
+ balance: ['workflow', 'automation', 'middleware'],
35
+ push: ['workflow', 'automation']
35
36
  };
36
37
  function asArray(value) {
37
38
  return Array.isArray(value) ? value : [];
@@ -84,6 +85,31 @@ function carriesValue(value) {
84
85
  * Etiketteki "(loop çocuğu)" bulguyu okuyan
85
86
  * ajanı adımın `rule.stepRule`'una yönlendirir.
86
87
  */
88
+ /**
89
+ * WF-036 kapısının sorusu alt akışlarla birlikte: tanım ya da `workflow` adımıyla (loop çocuğu dahil)
90
+ * çağırdığı akışlardan biri `push` taşıyor mu. Her akış bir kez okunur (döngüye karşı). Okunamayan alt
91
+ * akış hata fırlatır — çağıran kapı kapalı kalır.
92
+ */
93
+ export async function pushReachable(root, loadWorkflow) {
94
+ const pending = [root];
95
+ const visited = new Set();
96
+ while (pending.length > 0) {
97
+ const body = workflowBody(pending.pop(), 'automation');
98
+ if (stepCallSites(body, 'push').length > 0)
99
+ return true;
100
+ for (const { rule } of stepCallSites(body, 'workflow')) {
101
+ const workflowId = typeof rule.workflowId === 'string' ? rule.workflowId : '';
102
+ if (workflowId === '' || visited.has(workflowId))
103
+ continue;
104
+ visited.add(workflowId);
105
+ const child = await loadWorkflow(workflowId);
106
+ if (!child)
107
+ throw new Error(`alt workflow '${workflowId}' okunamadı`);
108
+ pending.push(child);
109
+ }
110
+ }
111
+ return false;
112
+ }
87
113
  function stepCallSites(body, type) {
88
114
  return workflowSteps(body).flatMap((step) => {
89
115
  const rule = (step.rule ?? {});
@@ -0,0 +1,72 @@
1
+ // `//` önünde `:` (https://), `(` (url(//cdn)), tırnak (src="//cdn"), eğik çizgi ya da harf varsa yorum değildir.
2
+ const commentPattern = /([ \t]*)(\/\*[\s\S]*?\*\/|<!--[\s\S]*?-->|(?<![:/\\(\w'"`])\/\/[^\n]*)(\n?)/g;
3
+ const LEAKS = [
4
+ { label: 'dal adı', pattern: /\b(?:feature|feat|fix|hotfix|bugfix|chore|refactor|release)\/[\w.-]+/i },
5
+ { label: 'kaynak dizini', pattern: /\bzeyrek-(?:frontend|backend|mcp|monitoring|qm-extension)\b|\bng-apps-[a-z][\w-]*|\b(?:apps|packages|src|Contexts)\/[\w.*-]+|(?:^|[\s(])\.(?:superpowers|claude)\b/i },
6
+ { label: 'kaynak dosyası', pattern: /\b[\w*-]+\.(?:svelte|cs|csproj|tsx?|mts|cts|py)\b/i },
7
+ { label: 'çatı adı', pattern: /\bsvelte\b/i },
8
+ { label: 'mimari karar kaydı', pattern: /\bADR[\s-]?\d{2,4}\b/i }
9
+ ];
10
+ const EXCERPT_RADIUS = 40;
11
+ function mayHoldComment(text) {
12
+ return text.includes('/*') || text.includes('//') || text.includes('<!--');
13
+ }
14
+ export function commentsIn(text) {
15
+ if (!mayHoldComment(text))
16
+ return [];
17
+ return [...text.matchAll(commentPattern)].map(match => match[2]);
18
+ }
19
+ function leaksInComment(comment, path) {
20
+ return LEAKS.flatMap(({ label, pattern }) => {
21
+ const match = pattern.exec(comment);
22
+ if (!match)
23
+ return [];
24
+ const start = Math.max(0, match.index - EXCERPT_RADIUS);
25
+ const end = Math.min(comment.length, match.index + match[0].length + EXCERPT_RADIUS);
26
+ return [{ path, label, excerpt: comment.slice(start, end).replace(/\s+/g, ' ').trim() }];
27
+ });
28
+ }
29
+ function walk(value, path, leaks) {
30
+ if (typeof value === 'string') {
31
+ for (const comment of commentsIn(value))
32
+ leaks.push(...leaksInComment(comment, path));
33
+ return;
34
+ }
35
+ if (Array.isArray(value)) {
36
+ value.forEach((item, index) => walk(item, `${path}[${index}]`, leaks));
37
+ return;
38
+ }
39
+ if (value && typeof value === 'object') {
40
+ for (const [key, item] of Object.entries(value))
41
+ walk(item, path === '' ? key : `${path}.${key}`, leaks);
42
+ }
43
+ }
44
+ export function sourceLeaksIn(value) {
45
+ const leaks = [];
46
+ walk(value, '', leaks);
47
+ return leaks;
48
+ }
49
+ const LISTED = 5;
50
+ export function sourceLeakFindings(leaks) {
51
+ const findings = leaks.slice(0, LISTED).map(leak => ({
52
+ ruleId: 'SRC-001',
53
+ severity: 'block',
54
+ message: `${leak.path || 'gövde'}: yorumda platform ${leak.label} geçiyor — \`${leak.excerpt}\`. Uygulama şemasına yorum dahil platform kaynak kodu bilgisi (dizin, dosya, dal, çatı adı, ADR) yazılmaz: yorumu kaldırın ya da atfı çıkarın; gerekçe repodaki docs/apps/<uygulama>/ dokümanına yazılır`
55
+ }));
56
+ if (leaks.length > LISTED)
57
+ findings.push({ ruleId: 'SRC-001', severity: 'block', message: `… ve ${leaks.length - LISTED} yerde daha` });
58
+ return findings;
59
+ }
60
+ /**
61
+ * Temizlikte yorumun yalnız sızan parçası değil tamamı silinir (kullanıcı, 2026-11-03): parça silmek yarım cümle
62
+ * bırakır. Satırın tek içeriği olan yorum satırıyla birlikte gider; kodun yanındaki yorum önündeki boşlukla gider.
63
+ */
64
+ export function stripSourceLeakComments(text) {
65
+ if (!mayHoldComment(text))
66
+ return text;
67
+ return text.replace(commentPattern, (match, _lead, comment, newline, offset, whole) => {
68
+ if (leaksInComment(comment, '').length === 0)
69
+ return match;
70
+ return offset === 0 || whole[offset - 1] === '\n' ? '' : newline;
71
+ });
72
+ }
@@ -4,6 +4,8 @@ import { AsyncLocalStorage } from 'node:async_hooks';
4
4
  import { TokenProvider } from './token.js';
5
5
  import { randomUUID } from 'node:crypto';
6
6
  import { request } from 'undici';
7
+ import { PolicyBlockError } from '../knowledge/policy.js';
8
+ import { sourceLeakFindings, sourceLeaksIn } from '../knowledge/sourceLeak.js';
7
9
  export const requestAuthStorage = new AsyncLocalStorage();
8
10
  const hiddenResponseHeaders = new Set(['set-cookie', 'authorization', 'proxy-authorization']);
9
11
  /**
@@ -140,6 +142,12 @@ export class Platform {
140
142
  return `Bearer ${await this.tokens.getToken()}`;
141
143
  }
142
144
  async send(api, method, path, options, raw = false, isRetry = false, networkAttempt = 1) {
145
+ // SRC-001 (2026-11-03): her yazma yolu buradan geçer; araç başına kapı bir yolu atlardı. Mutlak kapıdır.
146
+ if (options.body !== undefined && method !== 'GET' && method !== 'HEAD' && !isRetry && networkAttempt === 1) {
147
+ const leaks = sourceLeaksIn(options.body);
148
+ if (leaks.length > 0)
149
+ throw new PolicyBlockError(sourceLeakFindings(leaks), false);
150
+ }
143
151
  const baseUrl = api === 'org' ? this.config.orgApiBaseUrl : this.config.tenantApiBaseUrl;
144
152
  const context = { api, method, path, baseUrl, environment: options.environment, authMode: this.config.authMode };
145
153
  const url = new URL(`${baseUrl}${path}`);
@@ -101,6 +101,10 @@ yeniden girilebilir yaz ya da `onCleanup` kullan.
101
101
  - ✘ `set_event` ile `kaydet` olayına bağ kurmaya çalışmak (PAGE-008 reddeder). ✔ `actions`
102
102
  satırının `flow` prop'u.
103
103
  - ✘ `setInterval`'i temizliksiz kurmak (içerik değişince ikinci kopya başlar). ✔ `onCleanup`.
104
+ - ✘ CSS/JS/HTML yorumunda platformun kaynak kodunu anmak: dizin (`ng-apps-ui`, `packages/…`), dosya
105
+ (`Table.svelte`, `HttpStep.cs`), dal (`feature/…`), çatı adı (Svelte), ADR. ✔ Yorum yalnız uygulamanın
106
+ kendi diliyle ne yapıldığını söyler; gerekçe ve kaynak uygulamanın repodaki dokümanına yazılır.
107
+ Gateway böyle bir yorum taşıyan **her** yazmayı durdurur (SRC-001, `policyOverride` geçmez).
104
108
 
105
109
  Kaynak: `zeyrek-frontend/packages/ng-apps-ui/src/components/html/*` (Html.svelte, html.functions.ts,
106
110
  schema.ts) ve `zeyrek-frontend/docs/features/html-component.md`.
@@ -459,6 +459,7 @@ değişiminde koşar. Bağ: `page_update` `events: [{ name: 'focus', value: <flo
459
459
  Sunucudan gönderilen bildirim yerel `notification-schedule`'dan ayrıdır. Kanal ikiliye bağlıdır (NTF-004):
460
460
  `expo` = oynatıcı (**dev build** gerekir, Expo Go almaz), `apns` / `fcm` = mağaza sürümü iOS / Android.
461
461
 
462
+ - **Olaya bağlı, kişiye özel bildirim workflow'un `push` adımıdır** (`platform_guide('workflow')` §Push adımı); giriş yapan kullanıcının telefonu ona bağlanır.
462
463
  - **Kayıt kendiliğindendir.** Uygulama açılınca cihazını anonim kaydeder (kanal, dil, platform, izin, token);
463
464
  sayfaya adım eklenmez. Mağaza sürümü yalnız production'a kaydolur.
464
465
  - **Mağaza sürümünün push kimlikleri insanın işidir** (Studio › Yayınla; ajan yükleyemez). Durum:
@@ -213,6 +213,7 @@ olan ilk adıma** odaklanmaktır; hata yoksa son adıma. Aynı okumayı sen de y
213
213
  | Middleware ve `user` | koşmaz | koşar | **koşmaz** — `user` yok |
214
214
  | `email`, `http`, `code` içi HTTP | **gerçekten koşar** (SMTP gönderilir, dış uç çağrılır) | gerçek | gerçek |
215
215
  | `microfunction` | `preview: true` bayrağıyla çağrılır | gerçek | gerçek |
216
+ | `push` | **göndermez** — yayın commit'e bağlı, rollback onu düşürür; çıktı `deviceCount` verir, `notificationId` geri alınan kaydındır (aranmaz) | gerçek | gerçek |
216
217
  | `table` / `file` yazmaları | geri alınır | yukarıdaki commit/rollback | yukarıdaki commit/rollback |
217
218
  | Adım izi | **tam** (bu bölüm) | yok — yalnız yanıt gövdesi; ağaç için `microservice_debug` | yok — geçmiş satırı `status + süre + errorMessage` (`Step 'x' failed: <sebep>` ya da `The job's workflow returned 500 (<code>): <mesaj>`) |
218
219
 
@@ -403,6 +404,28 @@ dalına** gider; `BalanceInsufficientException` kodu `errorCode` olarak okunur.
403
404
  istemci tarafı `Entitlements.<hak>` yalnız ekranı gizler, sunucu denetimi bunun yerine geçmez.
404
405
  - Önizleme (`workflow_preview`) işlemi geri alır: `grant`/`spend` gerçekten koşar ve kalıcı satır bırakmaz.
405
406
 
407
+ ## Push adımı: kullanıcılara uzak bildirim (kök ADR 0032, `StepType.Push`)
408
+
409
+ Workflow ya da otomasyon, olay anında BELİRLİ uygulama kullanıcılarına (ya da kurulumlara) push gönderir. Girdi tek
410
+ `rule.expression` nesne literalidir:
411
+ `({ application?, to: { users?, installations? }, title, body, defaultLanguage?, path?, query?, badge? })`.
412
+
413
+ - `application` (slug) ya da `applicationId`; ikisi de boşsa workflow'un kendi uygulaması (mobil değilse 404, hata dalı).
414
+ - `to.users` uygulama kullanıcı kimlikleri, `to.installations` kurulum kimlikleri; ikisi **birleşim** (VEYA) olarak
415
+ hedeflenir, toplam 1–500. Daha büyük kitle için `loop`; "herkese" gönderim adımda yok, Studio › Bildirimler'de.
416
+ - `title` / `body` metin ya da `{ dil: metin }`; varsayılan dilde (`defaultLanguage`, boşsa projenin) başlık zorunlu,
417
+ ≤ 200 / ≤ 2000. Her telefon kendi dilini alır, yoksa varsayılanı.
418
+ - `path` dokununca açılacak sayfa; `query` nesne (`{ id: 42 }` → `id=42`) ya da metin.
419
+ - Çıktı `{ notificationId, deviceCount }`. **Telefonu olmayan kullanıcı hata değildir:** `deviceCount: 0`,
420
+ `notificationId: null`, başarı dalı. Biçim hatası 400 (parametre adı iletide: `To`, `Title`…) → hata dalı.
421
+ - **Kullanıcının telefonu nasıl bilinir:** kullanıcı uygulamada giriş yapınca telefon ona bağlanır, çıkışta (aynı
422
+ oturumun) bağı kalkar. Yalnız `Complex` erişim modelli projede; `simple` projede yalnız `installations` işe yarar.
423
+ Bağı `notification_device_list` (arama: e-posta) gösterir.
424
+ - **Önizleme göndermez** (yukarıdaki tablo; `email`'den farkı). Canlı uç, otomasyon ve zamanlanmış iş gönderir.
425
+ - **WF-036:** `push` taşıyan workflow'u (alt workflow'lar dahil) AI production'da `microservice_execute` ya da
426
+ `automation_trigger` ile çalıştıramaz — istek gitmeden reddedilir. Development'ta deneyin.
427
+ - `to.users` GUID olmalı; uygulama `applicationId` ile verilirse o da mobil olmalı (değilse 404, hata dalı).
428
+
406
429
  ## Step tipi × workflow tipi izin matrisi
407
430
 
408
431
  | Step | workflow | automation | middleware |
@@ -414,6 +437,7 @@ dalına** gider; `BalanceInsufficientException` kodu `errorCode` olarak okunur.
414
437
  | revert (Fail and Rollback) | – | ✓ | – |
415
438
  | process (BPM süreci) | ✓ | ✓ | – |
416
439
  | balance (uygulama bakiyesi / hak denetimi) | ✓ | ✓ | ✓ |
440
+ | push (kullanıcılara uzak bildirim) | ✓ | ✓ | – |
417
441
 
418
442
  > **`process` adımı süreci başlatır ya da ona sinyal/vekalet yazar, BEKLEMEZ (2026-09-18).**
419
443
  > Kural alanları `processSlug`, `processOperation: start | signal | delegate` (boş = `start`) ve
@@ -1,6 +1,6 @@
1
1
  {
2
- "hash": "34361b013283a252757338ed07d8cdcd70294df1c334d35723a9b194c62d1c4b",
3
- "generatedAt": "2026-11-03T11:41:34.837Z",
2
+ "hash": "68157be9624742c7086f9e40a9c8fb5565d612d82bf0a7d7d1ae063044ac7465",
3
+ "generatedAt": "2026-11-03T19:09:26.033Z",
4
4
  "types": 377,
5
5
  "files": 196,
6
6
  "note": "Şemaları üreten backend kaynaklarının parmak izi. Elle düzenlemeyin: 'npm run generate-schemas' yazar, 'npm run schema:check' denetler.",
@@ -9,4 +9,4 @@
9
9
  "System.Text.Json.Nodes.JsonNode",
10
10
  "System.Text.Json.Nodes.JsonNodeOptions"
11
11
  ]
12
- }
12
+ }
@@ -234,7 +234,8 @@
234
234
  "set-user",
235
235
  "microfunction",
236
236
  "process",
237
- "balance"
237
+ "balance",
238
+ "push"
238
239
  ],
239
240
  "type": "string",
240
241
  "format": ""
@@ -172,7 +172,8 @@
172
172
  "set-user",
173
173
  "microfunction",
174
174
  "process",
175
- "balance"
175
+ "balance",
176
+ "push"
176
177
  ],
177
178
  "type": "string",
178
179
  "format": ""
@@ -77,7 +77,8 @@
77
77
  "set-user",
78
78
  "microfunction",
79
79
  "process",
80
- "balance"
80
+ "balance",
81
+ "push"
81
82
  ],
82
83
  "type": "string",
83
84
  "format": ""
@@ -234,7 +234,8 @@
234
234
  "set-user",
235
235
  "microfunction",
236
236
  "process",
237
- "balance"
237
+ "balance",
238
+ "push"
238
239
  ],
239
240
  "type": "string",
240
241
  "format": ""
@@ -7,6 +7,7 @@ import { languageHeader } from '../knowledge/languageCodes.js';
7
7
  import { handle } from '../platform/responses.js';
8
8
  import { z } from 'zod';
9
9
  import { constraintSummary } from '../knowledge/backendConstraints.js';
10
+ import { assertNoPushInProduction } from './workflows.js';
10
11
  const policyOverrideParam = z.boolean().optional()
11
12
  .describe('true: politika block bulgularını warn\'a indirir (bilinçli istisna; yanıtta işaretlenir)');
12
13
  export function registerAutomationTools(server, platform) {
@@ -106,6 +107,15 @@ export function registerAutomationTools(server, platform) {
106
107
  }, handle(config, async ({ project, environment, entityId, id, operation, recordIds }) => {
107
108
  const resolver = new RefResolver(platform, project, environment);
108
109
  const resolvedEntityId = await resolveEntityId(resolver, entityId);
110
+ let automationApplicationId = '';
111
+ await assertNoPushInProduction('automation_trigger', environment, config, () => platform.tenant('GET', project, `/entities/${resolvedEntityId}/automation/${id}`, { environment }).then(payload => {
112
+ const record = payload;
113
+ const body = ((record?.data ?? record) ?? {});
114
+ automationApplicationId = String(body.applicationId ?? '');
115
+ return payload;
116
+ }), workflowId => automationApplicationId === ''
117
+ ? Promise.reject(new Error('automation applicationId taşımıyor'))
118
+ : platform.tenant('GET', project, `/workflows/${automationApplicationId}/${workflowId}`, { environment }));
109
119
  return withResolved(await platform.tenant('POST', project, `/entities/${resolvedEntityId}/automation/${id}/trigger`, {
110
120
  environment,
111
121
  body: { operation, recordIds: recordIds ?? [] }
@@ -16,6 +16,7 @@ import { PlatformApiError } from '../platform/errors.js';
16
16
  import { existingMatch, ifExistsParam, listAll, platformRename, withOutcome, withRename } from '../platform/ifExists.js';
17
17
  import { outlineWrite, recordWriteViewParam } from '../platform/writeView.js';
18
18
  import { handle } from '../platform/responses.js';
19
+ import { assertNoPushInProduction } from './workflows.js';
19
20
  import { headersForMode } from '../platform/http.js';
20
21
  import fastJsonPatch from 'fast-json-patch';
21
22
  import { jsonArray } from '../platform/jsonArray.js';
@@ -113,6 +114,19 @@ function hasAuthorization(headers) {
113
114
  * ucun ÖNÜNDE koşar ve ağaçta hiç görünmez — 09-04 koşusunda sessizce açık kalan şirket kapısı
114
115
  * (Y-03) bu yüzden adım ağacından teşhis edilemedi. Gateway zinciri kendisi çözüp listeler.
115
116
  */
117
+ /** Ucun workflow tanımı (WF-036 kapısı için); workflow kaynaklı değilse adımsız bir gövde. */
118
+ async function microserviceWorkflow(platform, project, environment, application, microservice) {
119
+ const applicationId = await new RefResolver(platform, project, environment).applicationId(application);
120
+ const list = await platform.tenant('GET', project, `/microservices/${applicationId}`, { environment, query: { pageIndex: 0, pageLength: 200 } });
121
+ const record = (list?.data ?? []).find(entry => String(entry.slug ?? '') === microservice);
122
+ if (!record)
123
+ throw new Error(`'${microservice}' ucu bulunamadı`);
124
+ const detail = await platform.tenant('GET', project, `/microservices/${applicationId}/${String(record.id ?? '')}`, { environment });
125
+ const definition = (detail?.data ?? detail ?? {});
126
+ if (typeof definition.workflowId !== 'string' || definition.workflowId === '')
127
+ return { steps: [] };
128
+ return platform.tenant('GET', project, `/workflows/${applicationId}/${definition.workflowId}`, { environment });
129
+ }
116
130
  async function middlewareChain(platform, project, environment, application, microservice, httpStatus) {
117
131
  try {
118
132
  const resolver = new RefResolver(platform, project, environment);
@@ -467,6 +481,7 @@ export function registerMicroserviceTools(server, platform) {
467
481
  },
468
482
  annotations: writes
469
483
  }, handle(config, async ({ project, environment, application, microservice, method, routeParams, query, body, parameters, headers, anonymous, asUser, asUserId, cookies, language, includeHeaders }) => {
484
+ await assertNoPushInProduction('microservice_execute', environment, config, () => microserviceWorkflow(platform, project, environment, application, microservice), async (workflowId) => platform.tenant('GET', project, `/workflows/${await new RefResolver(platform, project, environment).applicationId(application)}/${workflowId}`, { environment }));
470
485
  const notes = [];
471
486
  const eye = await impersonationHeaders(platform, 'microservice_execute', project, environment, { asUserId, asUser, anonymous, ownAuthorization: hasAuthorization(headers) }, { runtime: true });
472
487
  const member = anonymous === true ? asUser : await resolveIdentity(asUser, platform, project, environment);
@@ -4,7 +4,8 @@ import { evaluatePolicy, enforcePolicy, attachFindings } from '../knowledge/poli
4
4
  import { workflowOutline, resolvePart, resolvePatchOperations } from '../knowledge/outlines.js';
5
5
  import { patchResult, baseVersionOf } from '../platform/patch.js';
6
6
  import { writeDiff, withWriteDiff } from '../platform/writeDiff.js';
7
- import { policyRules, microfunctionStepFindings, workflowStepFindings, undefinedInputFieldFindings } from '../knowledge/rules.js';
7
+ import { policyRules, microfunctionStepFindings, workflowStepFindings, undefinedInputFieldFindings, pushReachable } from '../knowledge/rules.js';
8
+ import { PolicyBlockError } from '../knowledge/policy.js';
8
9
  import { identityMiddlewareFindings } from '../knowledge/access.js';
9
10
  import { languageHeader } from '../knowledge/languageCodes.js';
10
11
  import { handle } from '../platform/responses.js';
@@ -73,6 +74,32 @@ async function enrichMiddlewareVariables(platform, project, environment, applica
73
74
  };
74
75
  });
75
76
  }
77
+ /**
78
+ * WF-036 (kök ADR 0032): `push` adımı taşıyan bir akışı AI production'da çalıştırmaz ve tetiklemez —
79
+ * bildirim kullanıcının telefonuna gider, geri alınamaz. Tanım YALNIZ production'da okunur ve `workflow`
80
+ * adımlarının çağırdığı alt akışlar da izlenir (`pushReachable`); biri okunamazsa kapı kapalı kalır.
81
+ * Önizleme kapıya girmez: push önizlemede göndermez (commit'e bağlı yayın).
82
+ */
83
+ export async function assertNoPushInProduction(tool, environment, config, load, loadWorkflow) {
84
+ if ((environment ?? config.defaultEnvironment) !== 'production')
85
+ return;
86
+ const bodyOf = (payload) => {
87
+ const record = payload;
88
+ return ((record?.data ?? record) ?? null);
89
+ };
90
+ const definition = await load().then(bodyOf, () => null);
91
+ const sends = definition === null
92
+ ? null
93
+ : await pushReachable(definition, workflowId => loadWorkflow(workflowId).then(bodyOf)).catch(() => null);
94
+ if (sends === false)
95
+ return;
96
+ throw new PolicyBlockError([{
97
+ ruleId: 'WF-036',
98
+ severity: 'block',
99
+ message: `${tool}: push adımı taşıyan akış (alt workflow'lar dahil) production'da AI tarafından çalıştırılmaz — bildirim kullanıcıların telefonuna gider ve geri alınamaz.${sends === null ? ' Tanım ya da bir alt workflow okunamadı; kapı kapalı kalır.' : ''} Development'ta deneyin; production'da çalıştırmak insanın işidir.`,
100
+ guide: 'ngapps://guides/workflow'
101
+ }], false);
102
+ }
76
103
  export function registerWorkflowTools(server, platform) {
77
104
  const config = platform.config;
78
105
  server.registerTool('workflow_list', {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appaflytech/zeyrek-ai-mcp",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "type": "module",
5
5
  "description": "Zeyrek (NgApps) platform APIs as MCP tools — data, logic and Studio design surface in one gateway",
6
6
  "license": "UNLICENSED",