daystrom 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +98 -0
  2. package/dist/airs/management.d.ts +21 -0
  3. package/dist/airs/management.d.ts.map +1 -0
  4. package/dist/airs/management.js +78 -0
  5. package/dist/airs/management.js.map +1 -0
  6. package/dist/airs/scanner.d.ts +8 -0
  7. package/dist/airs/scanner.d.ts.map +1 -0
  8. package/dist/airs/scanner.js +28 -0
  9. package/dist/airs/scanner.js.map +1 -0
  10. package/dist/airs/types.d.ts +26 -0
  11. package/dist/airs/types.d.ts.map +1 -0
  12. package/dist/airs/types.js +6 -0
  13. package/dist/airs/types.js.map +1 -0
  14. package/dist/cli/commands/generate.d.ts +3 -0
  15. package/dist/cli/commands/generate.d.ts.map +1 -0
  16. package/dist/cli/commands/generate.js +127 -0
  17. package/dist/cli/commands/generate.js.map +1 -0
  18. package/dist/cli/commands/list.d.ts +3 -0
  19. package/dist/cli/commands/list.d.ts.map +1 -0
  20. package/dist/cli/commands/list.js +22 -0
  21. package/dist/cli/commands/list.js.map +1 -0
  22. package/dist/cli/commands/report.d.ts +3 -0
  23. package/dist/cli/commands/report.d.ts.map +1 -0
  24. package/dist/cli/commands/report.js +57 -0
  25. package/dist/cli/commands/report.js.map +1 -0
  26. package/dist/cli/commands/resume.d.ts +3 -0
  27. package/dist/cli/commands/resume.d.ts.map +1 -0
  28. package/dist/cli/commands/resume.js +95 -0
  29. package/dist/cli/commands/resume.js.map +1 -0
  30. package/dist/cli/index.d.ts +3 -0
  31. package/dist/cli/index.d.ts.map +1 -0
  32. package/dist/cli/index.js +18 -0
  33. package/dist/cli/index.js.map +1 -0
  34. package/dist/cli/prompts.d.ts +6 -0
  35. package/dist/cli/prompts.d.ts.map +1 -0
  36. package/dist/cli/prompts.js +72 -0
  37. package/dist/cli/prompts.js.map +1 -0
  38. package/dist/cli/renderer.d.ts +15 -0
  39. package/dist/cli/renderer.d.ts.map +1 -0
  40. package/dist/cli/renderer.js +103 -0
  41. package/dist/cli/renderer.js.map +1 -0
  42. package/dist/config/loader.d.ts +3 -0
  43. package/dist/config/loader.d.ts.map +1 -0
  44. package/dist/config/loader.js +63 -0
  45. package/dist/config/loader.js.map +1 -0
  46. package/dist/config/schema.d.ts +72 -0
  47. package/dist/config/schema.d.ts.map +1 -0
  48. package/dist/config/schema.js +42 -0
  49. package/dist/config/schema.js.map +1 -0
  50. package/dist/core/constraints.d.ts +16 -0
  51. package/dist/core/constraints.d.ts.map +1 -0
  52. package/dist/core/constraints.js +77 -0
  53. package/dist/core/constraints.js.map +1 -0
  54. package/dist/core/loop.d.ts +23 -0
  55. package/dist/core/loop.d.ts.map +1 -0
  56. package/dist/core/loop.js +146 -0
  57. package/dist/core/loop.js.map +1 -0
  58. package/dist/core/metrics.d.ts +3 -0
  59. package/dist/core/metrics.d.ts.map +1 -0
  60. package/dist/core/metrics.js +38 -0
  61. package/dist/core/metrics.js.map +1 -0
  62. package/dist/core/types.d.ts +104 -0
  63. package/dist/core/types.d.ts.map +1 -0
  64. package/dist/core/types.js +5 -0
  65. package/dist/core/types.js.map +1 -0
  66. package/dist/index.d.ts +23 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +34 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/llm/prompts/analyze-results.d.ts +3 -0
  71. package/dist/llm/prompts/analyze-results.d.ts.map +1 -0
  72. package/dist/llm/prompts/analyze-results.js +38 -0
  73. package/dist/llm/prompts/analyze-results.js.map +1 -0
  74. package/dist/llm/prompts/generate-tests.d.ts +3 -0
  75. package/dist/llm/prompts/generate-tests.d.ts.map +1 -0
  76. package/dist/llm/prompts/generate-tests.js +33 -0
  77. package/dist/llm/prompts/generate-tests.js.map +1 -0
  78. package/dist/llm/prompts/generate-topic.d.ts +4 -0
  79. package/dist/llm/prompts/generate-topic.d.ts.map +1 -0
  80. package/dist/llm/prompts/generate-topic.js +32 -0
  81. package/dist/llm/prompts/generate-topic.js.map +1 -0
  82. package/dist/llm/prompts/improve-topic.d.ts +3 -0
  83. package/dist/llm/prompts/improve-topic.d.ts.map +1 -0
  84. package/dist/llm/prompts/improve-topic.js +50 -0
  85. package/dist/llm/prompts/improve-topic.js.map +1 -0
  86. package/dist/llm/provider.d.ts +15 -0
  87. package/dist/llm/provider.d.ts.map +1 -0
  88. package/dist/llm/provider.js +71 -0
  89. package/dist/llm/provider.js.map +1 -0
  90. package/dist/llm/schemas.d.ts +97 -0
  91. package/dist/llm/schemas.d.ts.map +1 -0
  92. package/dist/llm/schemas.js +22 -0
  93. package/dist/llm/schemas.js.map +1 -0
  94. package/dist/llm/service.d.ts +19 -0
  95. package/dist/llm/service.d.ts.map +1 -0
  96. package/dist/llm/service.js +177 -0
  97. package/dist/llm/service.js.map +1 -0
  98. package/dist/memory/diff.d.ts +4 -0
  99. package/dist/memory/diff.d.ts.map +1 -0
  100. package/dist/memory/diff.js +24 -0
  101. package/dist/memory/diff.js.map +1 -0
  102. package/dist/memory/extractor.d.ts +15 -0
  103. package/dist/memory/extractor.d.ts.map +1 -0
  104. package/dist/memory/extractor.js +129 -0
  105. package/dist/memory/extractor.js.map +1 -0
  106. package/dist/memory/injector.d.ts +8 -0
  107. package/dist/memory/injector.d.ts.map +1 -0
  108. package/dist/memory/injector.js +76 -0
  109. package/dist/memory/injector.js.map +1 -0
  110. package/dist/memory/prompts/extract-learnings.d.ts +3 -0
  111. package/dist/memory/prompts/extract-learnings.d.ts.map +1 -0
  112. package/dist/memory/prompts/extract-learnings.js +34 -0
  113. package/dist/memory/prompts/extract-learnings.js.map +1 -0
  114. package/dist/memory/schemas.d.ts +63 -0
  115. package/dist/memory/schemas.d.ts.map +1 -0
  116. package/dist/memory/schemas.js +13 -0
  117. package/dist/memory/schemas.js.map +1 -0
  118. package/dist/memory/store.d.ts +13 -0
  119. package/dist/memory/store.d.ts.map +1 -0
  120. package/dist/memory/store.js +88 -0
  121. package/dist/memory/store.js.map +1 -0
  122. package/dist/memory/types.d.ts +55 -0
  123. package/dist/memory/types.d.ts.map +1 -0
  124. package/dist/memory/types.js +6 -0
  125. package/dist/memory/types.js.map +1 -0
  126. package/dist/persistence/store.d.ts +12 -0
  127. package/dist/persistence/store.d.ts.map +1 -0
  128. package/dist/persistence/store.js +68 -0
  129. package/dist/persistence/store.js.map +1 -0
  130. package/dist/persistence/types.d.ts +17 -0
  131. package/dist/persistence/types.d.ts.map +1 -0
  132. package/dist/persistence/types.js +2 -0
  133. package/dist/persistence/types.js.map +1 -0
  134. package/package.json +57 -0
package/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # Daystrom
2
+
3
+ **Automated Prisma AIRS custom topic guardrail generator with iterative, self-improving refinement.**
4
+
5
+ Named after Dr. Richard Daystrom's self-learning M-5 multitronic unit from Star Trek TOS — a system designed to improve itself through experience. Daystrom generates, tests, evaluates, and refines Palo Alto Prisma AIRS custom topic guardrails in an autonomous loop, learning from each run to produce better results over time.
6
+
7
+ ## What It Does
8
+
9
+ Daystrom automates the creation and optimization of [Prisma AIRS](https://docs.paloaltonetworks.com/ai-runtime-security) custom topic guardrails — content detection rules that tell the AIRS scanner what prompts to block or allow. Instead of manually crafting topic definitions and testing them by hand, Daystrom:
10
+
11
+ 1. **Generates** a custom topic definition (name, description, up to 5 examples) using an LLM, informed by any prior learnings from previous runs
12
+ 2. **Deploys** the topic to a live Prisma AIRS security profile via the Management API (OAuth2)
13
+ 3. **Generates test cases** — balanced positive prompts (should trigger detection) and negative prompts (should not trigger)
14
+ 4. **Scans** all test prompts against the live AIRS Scan API with configurable concurrency
15
+ 5. **Evaluates** efficacy: true positive rate, true negative rate, accuracy, coverage (`min(TPR, TNR)`), and F1 score
16
+ 6. **Analyzes** false positives and false negatives using the LLM to identify patterns
17
+ 7. **Improves** the topic definition iteratively — refining description and examples while keeping the topic name locked
18
+ 8. **Learns** — after the loop completes, extracts actionable insights and persists them for future runs on similar topics
19
+
20
+ The loop runs until coverage reaches a target threshold (default 90%) or max iterations (default 20) are exhausted.
21
+
22
+ ## Quick Start
23
+
24
+ ```bash
25
+ # Install
26
+ pnpm install
27
+
28
+ # Configure credentials
29
+ cp .env.example .env
30
+ # Edit .env — you need: ANTHROPIC_API_KEY (or other LLM provider),
31
+ # PANW_AI_SEC_API_KEY, PANW_MGMT_CLIENT_ID, PANW_MGMT_CLIENT_SECRET, PANW_MGMT_TSG_ID
32
+
33
+ # Run interactively (prompts for topic, profile, intent, etc.)
34
+ pnpm run generate
35
+
36
+ # Run non-interactively
37
+ pnpm run generate \
38
+ --provider claude-api \
39
+ --profile my-security-profile \
40
+ --topic "Block discussions about building explosives" \
41
+ --intent block \
42
+ --target-coverage 90
43
+ ```
44
+
45
+ ## Commands
46
+
47
+ All commands use `daystrom` as the binary name (or `pnpm run dev` in development):
48
+
49
+ | Command | Description |
50
+ |---------|-------------|
51
+ | `daystrom generate` | Start a new guardrail generation loop |
52
+ | `daystrom resume <runId>` | Resume a paused or failed run with additional iterations |
53
+ | `daystrom report <runId>` | View detailed results for a saved run (best or specific iteration) |
54
+ | `daystrom list` | List all saved runs with status and coverage |
55
+
56
+ ### Generate Flags
57
+
58
+ | Flag | Default | Description |
59
+ |------|---------|-------------|
60
+ | `--provider <name>` | `claude-api` | LLM provider (`claude-api`, `claude-vertex`, `claude-bedrock`, `gemini-api`, `gemini-vertex`, `gemini-bedrock`) |
61
+ | `--model <name>` | per-provider | Override the default model |
62
+ | `--profile <name>` | (prompted) | AIRS security profile name to attach the topic to |
63
+ | `--topic <desc>` | (prompted) | Natural language description of what to detect |
64
+ | `--intent <block\|allow>` | `block` | Whether matching prompts should be blocked or allowed |
65
+ | `--max-iterations <n>` | `20` | Maximum refinement iterations |
66
+ | `--target-coverage <n>` | `90` | Coverage percentage to stop at |
67
+ | `--no-memory` | memory on | Disable cross-run learning for this run |
68
+
69
+ ## Documentation
70
+
71
+ Full documentation: **[cdot65.github.io/daystrom](https://cdot65.github.io/daystrom/)**
72
+
73
+ ## Tech Stack
74
+
75
+ - **TypeScript ESM** on Node.js 20+ with strict mode
76
+ - **LangChain.js** — Claude (Anthropic API, Vertex, Bedrock) and Gemini (API, Vertex, Bedrock) with structured output via Zod schemas
77
+ - **Prisma AIRS SDK** (`@cdot65/prisma-airs-sdk@^0.2.0`) — scan API + management API (OAuth2 client credentials)
78
+ - **Commander.js** — CLI framework with 4 subcommands
79
+ - **Vitest** + **MSW** — 165 tests across 17 files (~98% stmt coverage)
80
+ - **Biome** — linting and formatting
81
+ - **Zod** — config validation, LLM output parsing, learning extraction schemas
82
+
83
+ ## Project Structure
84
+
85
+ ```
86
+ src/
87
+ ├── cli/ CLI entry, commands (generate/resume/report/list), prompts, renderer
88
+ ├── config/ Zod-validated config schema + env/file/CLI cascade loader
89
+ ├── core/ Async generator loop, efficacy metrics, AIRS topic constraints
90
+ ├── llm/ LangChain provider factory, structured output service, prompt templates
91
+ ├── airs/ Scanner (sync scan + batch) and Management (CRUD + profile linking) services
92
+ ├── memory/ Learning store, extractor, budget-aware injector, iteration diff
93
+ └── persistence/ JSON file store for run state
94
+ ```
95
+
96
+ ## License
97
+
98
+ MIT
@@ -0,0 +1,21 @@
1
+ import { type CreateCustomTopicRequest, type ManagementClientOptions, type CustomTopic as SdkCustomTopic } from '@cdot65/prisma-airs-sdk';
2
+ import type { ManagementService } from './types.js';
3
+ /**
4
+ * Wraps the SDK's ManagementClient to implement our ManagementService interface.
5
+ * OAuth2 token management, caching, and retry are handled by the SDK.
6
+ */
7
+ export declare class SdkManagementService implements ManagementService {
8
+ private client;
9
+ constructor(opts?: ManagementClientOptions);
10
+ createTopic(request: CreateCustomTopicRequest): Promise<SdkCustomTopic>;
11
+ updateTopic(topicId: string, request: CreateCustomTopicRequest): Promise<SdkCustomTopic>;
12
+ deleteTopic(topicId: string): Promise<void>;
13
+ listTopics(): Promise<SdkCustomTopic[]>;
14
+ /**
15
+ * Sets a single custom topic on a profile's topic-guardrails config.
16
+ * Replaces any existing topics so only the current topic is evaluated.
17
+ * Previous runs may have left stale topics — this clears them.
18
+ */
19
+ assignTopicToProfile(profileName: string, topicId: string, topicName: string, action: 'allow' | 'block'): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=management.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"management.d.ts","sourceRoot":"","sources":["../../src/airs/management.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,wBAAwB,EAE7B,KAAK,uBAAuB,EAC5B,KAAK,WAAW,IAAI,cAAc,EACnC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;GAGG;AACH,qBAAa,oBAAqB,YAAW,iBAAiB;IAC5D,OAAO,CAAC,MAAM,CAAmB;gBAErB,IAAI,CAAC,EAAE,uBAAuB;IAIpC,WAAW,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIvE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC;IAIxF,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3C,UAAU,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAK7C;;;;OAIG;IACG,oBAAoB,CACxB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,GAAG,OAAO,GACxB,OAAO,CAAC,IAAI,CAAC;CAsDjB"}
@@ -0,0 +1,78 @@
1
+ import { ManagementClient, } from '@cdot65/prisma-airs-sdk';
2
+ /**
3
+ * Wraps the SDK's ManagementClient to implement our ManagementService interface.
4
+ * OAuth2 token management, caching, and retry are handled by the SDK.
5
+ */
6
+ export class SdkManagementService {
7
+ client;
8
+ constructor(opts) {
9
+ this.client = new ManagementClient(opts);
10
+ }
11
+ async createTopic(request) {
12
+ return this.client.topics.create(request);
13
+ }
14
+ async updateTopic(topicId, request) {
15
+ return this.client.topics.update(topicId, request);
16
+ }
17
+ async deleteTopic(topicId) {
18
+ await this.client.topics.delete(topicId);
19
+ }
20
+ async listTopics() {
21
+ const response = await this.client.topics.list();
22
+ return response.custom_topics;
23
+ }
24
+ /**
25
+ * Sets a single custom topic on a profile's topic-guardrails config.
26
+ * Replaces any existing topics so only the current topic is evaluated.
27
+ * Previous runs may have left stale topics — this clears them.
28
+ */
29
+ async assignTopicToProfile(profileName, topicId, topicName, action) {
30
+ // Find profile by name
31
+ const { ai_profiles } = await this.client.profiles.list();
32
+ const profile = ai_profiles.find((p) => p.profile_name === profileName);
33
+ if (!profile?.profile_id) {
34
+ throw new Error(`Profile "${profileName}" not found`);
35
+ }
36
+ // Deep clone the policy to mutate
37
+ const policy = JSON.parse(JSON.stringify(profile.policy ?? {}));
38
+ const aiProfiles = policy['ai-security-profiles'] ?? [
39
+ { 'model-type': 'default', 'model-configuration': {} },
40
+ ];
41
+ const modelConfig = aiProfiles[0]?.['model-configuration'] ?? {};
42
+ // Find or create model-protection with topic-guardrails
43
+ const modelProtection = modelConfig['model-protection'] ?? [];
44
+ let topicGuardrails = modelProtection.find((mp) => mp.name === 'topic-guardrails');
45
+ if (!topicGuardrails) {
46
+ topicGuardrails = {
47
+ action: 'allow',
48
+ name: 'topic-guardrails',
49
+ options: [],
50
+ 'topic-list': [],
51
+ };
52
+ modelProtection.push(topicGuardrails);
53
+ }
54
+ // Replace the entire topic-list with only the current topic under the given action.
55
+ // The opposite action gets an empty list to ensure no stale topics remain.
56
+ const oppositeAction = action === 'block' ? 'allow' : 'block';
57
+ topicGuardrails['topic-list'] = [
58
+ {
59
+ action,
60
+ topic: [{ topic_id: topicId, topic_name: topicName, revision: 1 }],
61
+ },
62
+ {
63
+ action: oppositeAction,
64
+ topic: [],
65
+ },
66
+ ];
67
+ // Write back
68
+ modelConfig['model-protection'] = modelProtection;
69
+ aiProfiles[0]['model-configuration'] = modelConfig;
70
+ policy['ai-security-profiles'] = aiProfiles;
71
+ await this.client.profiles.update(profile.profile_id, {
72
+ profile_name: profile.profile_name,
73
+ active: profile.active,
74
+ policy,
75
+ });
76
+ }
77
+ }
78
+ //# sourceMappingURL=management.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"management.js","sourceRoot":"","sources":["../../src/airs/management.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ScanResult, ScanService } from './types.js';
2
+ export declare class AirsScanService implements ScanService {
3
+ private scanner;
4
+ constructor(apiKey: string);
5
+ scan(profileName: string, prompt: string, sessionId?: string): Promise<ScanResult>;
6
+ scanBatch(profileName: string, prompts: string[], concurrency?: number, sessionId?: string): Promise<ScanResult[]>;
7
+ }
8
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../../src/airs/scanner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE1D,qBAAa,eAAgB,YAAW,WAAW;IACjD,OAAO,CAAC,OAAO,CAA+B;gBAElC,MAAM,EAAE,MAAM;IAKpB,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBlF,SAAS,CACb,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,SAAI,EACf,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC;CAMzB"}
@@ -0,0 +1,28 @@
1
+ import { Content, init, Scanner } from '@cdot65/prisma-airs-sdk';
2
+ import pLimit from 'p-limit';
3
+ export class AirsScanService {
4
+ scanner;
5
+ constructor(apiKey) {
6
+ init({ apiKey });
7
+ this.scanner = new Scanner();
8
+ }
9
+ async scan(profileName, prompt, sessionId) {
10
+ const content = new Content({ prompt });
11
+ const response = await this.scanner.syncScan({ profile_name: profileName }, content, sessionId ? { sessionId } : undefined);
12
+ const action = response.action === 'block' ? 'block' : 'allow';
13
+ const detected = response.prompt_detected;
14
+ const triggered = !!(detected?.topic_guardrails_details || detected?.topic_violation);
15
+ return {
16
+ scanId: response.scan_id ?? '',
17
+ reportId: response.report_id ?? '',
18
+ action: action,
19
+ triggered,
20
+ raw: response,
21
+ };
22
+ }
23
+ async scanBatch(profileName, prompts, concurrency = 5, sessionId) {
24
+ const limit = pLimit(concurrency);
25
+ return Promise.all(prompts.map((prompt) => limit(() => this.scan(profileName, prompt, sessionId))));
26
+ }
27
+ }
28
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../../src/airs/scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,MAAM,MAAM,SAAS,CAAC;AAG7B,MAAM,OAAO,eAAe;IAClB,OAAO,CAA+B;IAE9C,YAAY,MAAc;QACxB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,WAAmB,EAAE,MAAc,EAAE,SAAkB;QAChE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QACxC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAC1C,EAAE,YAAY,EAAE,WAAW,EAAE,EAC7B,OAAO,EACP,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CACtC,CAAC;QAEF,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAC/D,MAAM,QAAQ,GAAG,QAAQ,CAAC,eAAsD,CAAC;QACjF,MAAM,SAAS,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,wBAAwB,IAAI,QAAQ,EAAE,eAAe,CAAC,CAAC;QAEtF,OAAO;YACL,MAAM,EAAE,QAAQ,CAAC,OAAO,IAAI,EAAE;YAC9B,QAAQ,EAAE,QAAQ,CAAC,SAAS,IAAI,EAAE;YAClC,MAAM,EAAE,MAA2B;YACnC,SAAS;YACT,GAAG,EAAE,QAAQ;SACd,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CACb,WAAmB,EACnB,OAAiB,EACjB,WAAW,GAAG,CAAC,EACf,SAAkB;QAElB,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QAClC,OAAO,OAAO,CAAC,GAAG,CAChB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC,CAChF,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * AIRS integration types — scan results and service interfaces for the
3
+ * Prisma AIRS scanner and topic management APIs.
4
+ */
5
+ import type { CreateCustomTopicRequest, CustomTopic as SdkCustomTopic } from '@cdot65/prisma-airs-sdk';
6
+ export type { CreateCustomTopicRequest, SdkCustomTopic };
7
+ export interface ScanResult {
8
+ scanId: string;
9
+ reportId: string;
10
+ action: 'allow' | 'block';
11
+ triggered: boolean;
12
+ category?: string;
13
+ raw?: unknown;
14
+ }
15
+ export interface ScanService {
16
+ scan(profileName: string, prompt: string, sessionId?: string): Promise<ScanResult>;
17
+ scanBatch(profileName: string, prompts: string[], concurrency?: number, sessionId?: string): Promise<ScanResult[]>;
18
+ }
19
+ export interface ManagementService {
20
+ createTopic(request: CreateCustomTopicRequest): Promise<SdkCustomTopic>;
21
+ updateTopic(topicId: string, request: CreateCustomTopicRequest): Promise<SdkCustomTopic>;
22
+ deleteTopic(topicId: string): Promise<void>;
23
+ listTopics(): Promise<SdkCustomTopic[]>;
24
+ assignTopicToProfile(profileName: string, topicId: string, topicName: string, action: 'allow' | 'block'): Promise<void>;
25
+ }
26
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/airs/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,WAAW,IAAI,cAAc,EAC9B,MAAM,yBAAyB,CAAC;AAKjC,YAAY,EAAE,wBAAwB,EAAE,cAAc,EAAE,CAAC;AAKzD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAKD,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACnF,SAAS,CACP,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EAAE,EACjB,WAAW,CAAC,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACxE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACzF,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,UAAU,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IACxC,oBAAoB,CAClB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,OAAO,GAAG,OAAO,GACxB,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * AIRS integration types — scan results and service interfaces for the
3
+ * Prisma AIRS scanner and topic management APIs.
4
+ */
5
+ export {};
6
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/airs/types.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerGenerateCommand(program: Command): void;
3
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BzC,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuH9D"}
@@ -0,0 +1,127 @@
1
+ import { SdkManagementService } from '../../airs/management.js';
2
+ import { AirsScanService } from '../../airs/scanner.js';
3
+ import { loadConfig } from '../../config/loader.js';
4
+ import { runLoop } from '../../core/loop.js';
5
+ import { createLlmProvider } from '../../llm/provider.js';
6
+ import { LangChainLlmService } from '../../llm/service.js';
7
+ import { LearningExtractor } from '../../memory/extractor.js';
8
+ import { MemoryInjector } from '../../memory/injector.js';
9
+ import { MemoryStore } from '../../memory/store.js';
10
+ import { JsonFileStore } from '../../persistence/store.js';
11
+ import { collectUserInput } from '../prompts.js';
12
+ import { renderAnalysis, renderError, renderHeader, renderIterationStart, renderIterationSummary, renderLoopComplete, renderMemoryExtracted, renderMemoryLoaded, renderMetrics, renderTestProgress, renderTopic, } from '../renderer.js';
13
+ export function registerGenerateCommand(program) {
14
+ program
15
+ .command('generate')
16
+ .description('Start a new guardrail generation loop')
17
+ .option('--provider <provider>', 'LLM provider')
18
+ .option('--model <model>', 'LLM model name')
19
+ .option('--profile <name>', 'AIRS security profile name')
20
+ .option('--topic <description>', 'Topic description')
21
+ .option('--intent <intent>', 'Intent: block or allow')
22
+ .option('--max-iterations <n>', 'Max iterations', '20')
23
+ .option('--target-coverage <n>', 'Target coverage %', '90')
24
+ .option('--memory', 'Enable learning memory (default)')
25
+ .option('--no-memory', 'Disable learning memory')
26
+ .action(async (opts) => {
27
+ try {
28
+ renderHeader();
29
+ const config = await loadConfig({
30
+ llmProvider: opts.provider,
31
+ llmModel: opts.model,
32
+ memoryEnabled: opts.memory !== undefined ? String(opts.memory) : undefined,
33
+ });
34
+ // Collect user input (interactive or from CLI flags)
35
+ let userInput;
36
+ if (opts.topic && opts.profile) {
37
+ userInput = {
38
+ topicDescription: opts.topic,
39
+ intent: (opts.intent ?? 'block'),
40
+ profileName: opts.profile,
41
+ maxIterations: Number.parseInt(opts.maxIterations, 10),
42
+ targetCoverage: Number.parseInt(opts.targetCoverage, 10) / 100,
43
+ };
44
+ }
45
+ else {
46
+ userInput = await collectUserInput();
47
+ }
48
+ // Initialize services
49
+ const model = await createLlmProvider({
50
+ provider: config.llmProvider,
51
+ model: config.llmModel,
52
+ anthropicApiKey: config.anthropicApiKey,
53
+ googleApiKey: config.googleApiKey,
54
+ googleCloudProject: config.googleCloudProject,
55
+ googleCloudLocation: config.googleCloudLocation,
56
+ awsRegion: config.awsRegion,
57
+ awsAccessKeyId: config.awsAccessKeyId,
58
+ awsSecretAccessKey: config.awsSecretAccessKey,
59
+ });
60
+ // Set up memory system
61
+ const memoryEnabled = config.memoryEnabled;
62
+ const memoryStore = memoryEnabled ? new MemoryStore(config.memoryDir) : undefined;
63
+ const memoryInjector = memoryStore
64
+ ? new MemoryInjector(memoryStore, config.maxMemoryChars)
65
+ : undefined;
66
+ const llm = new LangChainLlmService(model, memoryInjector);
67
+ if (!config.airsApiKey)
68
+ throw new Error('PANW_AI_SEC_API_KEY is required');
69
+ const scanner = new AirsScanService(config.airsApiKey);
70
+ const management = new SdkManagementService({
71
+ clientId: config.mgmtClientId,
72
+ clientSecret: config.mgmtClientSecret,
73
+ tsgId: config.mgmtTsgId,
74
+ apiEndpoint: config.mgmtEndpoint,
75
+ tokenEndpoint: config.mgmtTokenEndpoint,
76
+ });
77
+ const store = new JsonFileStore(config.dataDir);
78
+ // Load memory before loop
79
+ if (memoryEnabled) {
80
+ const learningCount = await llm.loadMemory(userInput.topicDescription);
81
+ renderMemoryLoaded(learningCount);
82
+ }
83
+ const memoryExtractor = memoryStore ? new LearningExtractor(model, memoryStore) : undefined;
84
+ // Run the loop
85
+ for await (const event of runLoop(userInput, {
86
+ llm,
87
+ management,
88
+ scanner,
89
+ propagationDelayMs: config.propagationDelayMs,
90
+ memory: memoryExtractor ? { extractor: memoryExtractor } : undefined,
91
+ })) {
92
+ switch (event.type) {
93
+ case 'iteration:start':
94
+ renderIterationStart(event.iteration);
95
+ break;
96
+ case 'generate:complete':
97
+ renderTopic(event.topic);
98
+ break;
99
+ case 'test:progress':
100
+ renderTestProgress(event.completed, event.total);
101
+ break;
102
+ case 'evaluate:complete':
103
+ renderMetrics(event.metrics);
104
+ break;
105
+ case 'analyze:complete':
106
+ renderAnalysis(event.analysis);
107
+ break;
108
+ case 'iteration:complete':
109
+ renderIterationSummary(event.result);
110
+ break;
111
+ case 'memory:extracted':
112
+ renderMemoryExtracted(event.learningCount);
113
+ break;
114
+ case 'loop:complete':
115
+ await store.save(event.runState);
116
+ renderLoopComplete(event.runState);
117
+ break;
118
+ }
119
+ }
120
+ }
121
+ catch (err) {
122
+ renderError(err instanceof Error ? err.message : String(err));
123
+ process.exit(1);
124
+ }
125
+ });
126
+ }
127
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/cli/commands/generate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerListCommand(program: Command): void;
3
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKzC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgB1D"}
@@ -0,0 +1,22 @@
1
+ import { loadConfig } from '../../config/loader.js';
2
+ import { JsonFileStore } from '../../persistence/store.js';
3
+ import { renderError, renderHeader, renderRunList } from '../renderer.js';
4
+ export function registerListCommand(program) {
5
+ program
6
+ .command('list')
7
+ .description('List all saved runs')
8
+ .action(async () => {
9
+ try {
10
+ renderHeader();
11
+ const config = await loadConfig();
12
+ const store = new JsonFileStore(config.dataDir);
13
+ const runs = await store.list();
14
+ renderRunList(runs);
15
+ }
16
+ catch (err) {
17
+ renderError(err instanceof Error ? err.message : String(err));
18
+ process.exit(1);
19
+ }
20
+ });
21
+ }
22
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/cli/commands/list.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE1E,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,qBAAqB,CAAC;SAClC,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,IAAI,CAAC;YACH,YAAY,EAAE,CAAC;YACf,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;YAChC,aAAa,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,WAAW,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerReportCommand(program: Command): void;
3
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsD5D"}
@@ -0,0 +1,57 @@
1
+ import chalk from 'chalk';
2
+ import { loadConfig } from '../../config/loader.js';
3
+ import { JsonFileStore } from '../../persistence/store.js';
4
+ import { renderAnalysis, renderError, renderHeader, renderMetrics, renderTopic, } from '../renderer.js';
5
+ export function registerReportCommand(program) {
6
+ program
7
+ .command('report <runId>')
8
+ .description('View detailed report for a run')
9
+ .option('--iteration <n>', 'Show specific iteration')
10
+ .action(async (runId, opts) => {
11
+ try {
12
+ renderHeader();
13
+ const config = await loadConfig();
14
+ const store = new JsonFileStore(config.dataDir);
15
+ const run = await store.load(runId);
16
+ if (!run) {
17
+ renderError(`Run ${runId} not found`);
18
+ process.exit(1);
19
+ }
20
+ console.log(chalk.bold(`\n Run: ${run.id}`));
21
+ console.log(` Status: ${run.status}`);
22
+ console.log(` Created: ${run.createdAt}`);
23
+ console.log(` Topic: ${run.userInput.topicDescription}`);
24
+ console.log(` Intent: ${run.userInput.intent}`);
25
+ console.log(` Best coverage: ${(run.bestCoverage * 100).toFixed(1)}% (iteration ${run.bestIteration})`);
26
+ console.log(` Total iterations: ${run.iterations.length}`);
27
+ if (opts.iteration) {
28
+ const idx = Number.parseInt(opts.iteration, 10) - 1;
29
+ const iter = run.iterations[idx];
30
+ if (!iter) {
31
+ renderError(`Iteration ${opts.iteration} not found`);
32
+ process.exit(1);
33
+ }
34
+ console.log(chalk.bold(`\n Iteration ${iter.iteration}:`));
35
+ renderTopic(iter.topic);
36
+ renderMetrics(iter.metrics);
37
+ renderAnalysis(iter.analysis);
38
+ }
39
+ else {
40
+ // Show best iteration
41
+ const best = run.iterations[run.bestIteration - 1];
42
+ if (best) {
43
+ console.log(chalk.bold(`\n Best Iteration (${best.iteration}):`));
44
+ renderTopic(best.topic);
45
+ renderMetrics(best.metrics);
46
+ renderAnalysis(best.analysis);
47
+ }
48
+ }
49
+ console.log();
50
+ }
51
+ catch (err) {
52
+ renderError(err instanceof Error ? err.message : String(err));
53
+ process.exit(1);
54
+ }
55
+ });
56
+ }
57
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../../../src/cli/commands/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Command } from 'commander';
2
+ export declare function registerResumeCommand(program: Command): void;
3
+ //# sourceMappingURL=resume.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resume.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/resume.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoBzC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2F5D"}