Skip to content

Commit d054d79

Browse files
committed
feat: save
1 parent f3c873d commit d054d79

26 files changed

Lines changed: 1613 additions & 422 deletions

apps/api/src/ai-agent/AI-README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,19 @@ The AI is NOT just a "replier" - it's a decision-making agent that chooses the b
4646

4747
3. **Layered Security**: Immutable security prompts sandwich the user-configurable base prompt, preventing prompt injection attacks.
4848

49-
4. **Behavior Settings**: Each AI agent can be configured with different capabilities and background analysis settings. Settings are persisted in the database and configurable via dashboard.
49+
4. **Prompt Governance**: `security.md` is immutable, `agent.md` is controlled by base prompt, and all other core policy docs are editable through prompt studio.
5050

51-
5. **BullMQ Execution**: All processing happens in BullMQ workers for reliability and scalability.
51+
5. **Behavior Settings**: Each AI agent can be configured with different capabilities and background analysis settings. Settings are persisted in the database and configurable via dashboard.
5252

53-
6. **Fast Response**: Queue delay is disabled; natural typing delays between messages keep responses human.
53+
6. **BullMQ Execution**: All processing happens in BullMQ workers for reliability and scalability.
5454

55-
7. **Audience-Aware Events**: Progress events have audience filtering (widget vs dashboard) for appropriate visibility.
55+
7. **Fast Response**: Queue delay is disabled; natural typing delays between messages keep responses human.
5656

57-
8. **Allowlist-Driven Tool Timeline Visibility**: Only allowlisted tools are treated as conversation-visible timeline activity; all other tools are persisted as log-only timeline rows.
57+
8. **Audience-Aware Events**: Progress events have audience filtering (widget vs dashboard) for appropriate visibility.
5858

59-
9. **AI SDK v6-Compatible Tool Metadata**: Tool linkage and classification metadata is stored under `callProviderMetadata.cossistant.toolTimeline` (with backward-compatible `providerMetadata` support), with no new DB schema fields.
59+
9. **Allowlist-Driven Tool Timeline Visibility**: Only allowlisted tools are treated as conversation-visible timeline activity; all other tools are persisted as log-only timeline rows.
60+
61+
10. **AI SDK v6-Compatible Tool Metadata**: Tool linkage and classification metadata is stored under `callProviderMetadata.cossistant.toolTimeline` (with backward-compatible `providerMetadata` support), with no new DB schema fields.
6062

6163
---
6264

@@ -131,6 +133,8 @@ apps/api/src/ai-agent/
131133
│ ├── system.ts # Dynamic system prompt (layered architecture)
132134
│ ├── security.ts # Core security prompts (immutable)
133135
│ ├── templates.ts # Reusable fragments
136+
│ ├── documents.ts # Core/skill document naming and editability rules
137+
│ ├── resolver.ts # Core + skill prompt resolution with overrides
134138
│ └── instructions.ts # Behavior instructions
135139
136140
├── tools/ # LLM tools
@@ -153,7 +157,7 @@ apps/api/src/ai-agent/
153157
│ ├── categorization.ts # Auto-categorize
154158
│ └── injection.ts # Prompt injection detection
155159
156-
├── output/ # Structured output
160+
├── output/ # Output parsing utilities (tool-loop compatibility)
157161
│ ├── schemas.ts # Zod schemas
158162
│ └── parser.ts # Parse & validate
159163

apps/api/src/ai-agent/behaviors/catalog.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
AiAgentBehaviorPromptDocumentName,
33
AiAgentBehaviorPromptId,
44
AiAgentBehaviorPromptPreset,
5+
AiAgentEditableCorePromptDocumentName,
56
} from "@cossistant/types";
67
import { PROMPT_TEMPLATES } from "../prompts/templates";
78

@@ -14,6 +15,13 @@ export type BehaviorPromptDefinition = {
1415
presets: readonly AiAgentBehaviorPromptPreset[];
1516
};
1617

18+
export type CorePromptStudioDefinition = {
19+
documentName: AiAgentEditableCorePromptDocumentName;
20+
label: string;
21+
description: string;
22+
presets: readonly AiAgentBehaviorPromptPreset[];
23+
};
24+
1725
const VISITOR_CONTACT_PRESETS = [
1826
{
1927
id: "contact_only_if_needed",
@@ -98,6 +106,52 @@ const BEHAVIOR_PROMPT_CATALOG = [
98106
},
99107
] as const satisfies readonly BehaviorPromptDefinition[];
100108

109+
const EDITABLE_CORE_PROMPT_CATALOG = [
110+
{
111+
documentName: "behaviour.md",
112+
label: "Response behavior",
113+
description:
114+
"Guidelines for escalation behavior and mode-aware response constraints.",
115+
presets: [],
116+
},
117+
{
118+
documentName: "participation.md",
119+
label: "Participation policy",
120+
description:
121+
"Rules for when the AI should reply versus stay silent in mixed human/AI conversations.",
122+
presets: [],
123+
},
124+
{
125+
documentName: "grounding.md",
126+
label: "Grounding policy",
127+
description:
128+
"Rules that require retrieval-first behavior for factual/product/policy responses.",
129+
presets: [],
130+
},
131+
{
132+
documentName: "capabilities.md",
133+
label: "Capabilities policy",
134+
description:
135+
"Defines what actions/tools the AI is allowed or disallowed to perform.",
136+
presets: [],
137+
},
138+
{
139+
documentName: "visitor-contact.md",
140+
label: "How and when to get visitor's contact details",
141+
description:
142+
"Define when and how the agent asks for visitor identity details (name/email).",
143+
presets: VISITOR_CONTACT_PRESETS,
144+
},
145+
{
146+
documentName: "decision.md",
147+
label:
148+
"How the agent should decide when to respond or not in a conversation",
149+
description:
150+
"Control the decision gate policy that determines when the AI should respond, observe, or assist the team.",
151+
presets: SMART_DECISION_PRESETS,
152+
},
153+
] as const satisfies readonly CorePromptStudioDefinition[];
154+
101155
const BEHAVIOR_PROMPT_CATALOG_BY_ID = new Map(
102156
BEHAVIOR_PROMPT_CATALOG.map((behavior) => [behavior.id, behavior])
103157
);
@@ -106,6 +160,10 @@ const BEHAVIOR_PROMPT_CATALOG_BY_DOCUMENT_NAME = new Map(
106160
BEHAVIOR_PROMPT_CATALOG.map((behavior) => [behavior.documentName, behavior])
107161
);
108162

163+
const EDITABLE_CORE_PROMPT_CATALOG_BY_DOCUMENT_NAME = new Map(
164+
EDITABLE_CORE_PROMPT_CATALOG.map((entry) => [entry.documentName, entry])
165+
);
166+
109167
export const EDITABLE_BEHAVIOR_CORE_DOCUMENT_NAMES = [
110168
...new Set(BEHAVIOR_PROMPT_CATALOG.map((behavior) => behavior.documentName)),
111169
] as const;
@@ -114,6 +172,10 @@ export function getBehaviorPromptCatalog(): readonly BehaviorPromptDefinition[]
114172
return BEHAVIOR_PROMPT_CATALOG;
115173
}
116174

175+
export function getEditableCorePromptCatalog(): readonly CorePromptStudioDefinition[] {
176+
return EDITABLE_CORE_PROMPT_CATALOG;
177+
}
178+
117179
export function getBehaviorPromptDefinition(
118180
behaviorId: AiAgentBehaviorPromptId
119181
): BehaviorPromptDefinition | null {
@@ -125,3 +187,11 @@ export function getBehaviorPromptDefinitionByDocumentName(
125187
): BehaviorPromptDefinition | null {
126188
return BEHAVIOR_PROMPT_CATALOG_BY_DOCUMENT_NAME.get(documentName) ?? null;
127189
}
190+
191+
export function getEditableCorePromptDefinitionByDocumentName(
192+
documentName: AiAgentEditableCorePromptDocumentName
193+
): CorePromptStudioDefinition | null {
194+
return (
195+
EDITABLE_CORE_PROMPT_CATALOG_BY_DOCUMENT_NAME.get(documentName) ?? null
196+
);
197+
}

apps/api/src/ai-agent/prompts/documents.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,17 @@ import {
99

1010
describe("prompt document rules", () => {
1111
it("accepts reserved core document names", () => {
12+
expect(isCorePromptDocumentName("behaviour.md")).toBe(true);
13+
expect(isCorePromptDocumentName("participation.md")).toBe(true);
14+
expect(isCorePromptDocumentName("grounding.md")).toBe(true);
1215
expect(isCorePromptDocumentName("capabilities.md")).toBe(true);
1316
expect(isCorePromptDocumentName("decision.md")).toBe(true);
1417
expect(isCorePromptDocumentName("visitor-contact.md")).toBe(true);
18+
expect(() => assertCorePromptDocumentName("behaviour.md")).not.toThrow();
19+
expect(() =>
20+
assertCorePromptDocumentName("participation.md")
21+
).not.toThrow();
22+
expect(() => assertCorePromptDocumentName("grounding.md")).not.toThrow();
1523
expect(() => assertCorePromptDocumentName("capabilities.md")).not.toThrow();
1624
expect(() => assertCorePromptDocumentName("decision.md")).not.toThrow();
1725
expect(() =>

apps/api/src/ai-agent/prompts/documents.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,28 @@ export const RESERVED_CORE_PROMPT_DOCUMENT_NAMES = new Set<string>(
1818
CORE_PROMPT_DOCUMENT_NAMES
1919
);
2020

21-
export const EDITABLE_BEHAVIOR_CORE_PROMPT_DOCUMENT_NAMES = [
21+
export const EDITABLE_CORE_PROMPT_DOCUMENT_NAMES = [
22+
"behaviour.md",
23+
"participation.md",
24+
"grounding.md",
25+
"capabilities.md",
2226
"visitor-contact.md",
2327
"decision.md",
2428
] as const;
2529

26-
export const EDITABLE_BEHAVIOR_CORE_PROMPT_DOCUMENT_NAME_SET = new Set<string>(
27-
EDITABLE_BEHAVIOR_CORE_PROMPT_DOCUMENT_NAMES
30+
export const EDITABLE_CORE_PROMPT_DOCUMENT_NAME_SET = new Set<string>(
31+
EDITABLE_CORE_PROMPT_DOCUMENT_NAMES
2832
);
2933

34+
/**
35+
* Backward-compatible aliases kept while behavior-specific studio endpoints
36+
* remain available as wrappers around the generic core prompt studio.
37+
*/
38+
export const EDITABLE_BEHAVIOR_CORE_PROMPT_DOCUMENT_NAMES =
39+
EDITABLE_CORE_PROMPT_DOCUMENT_NAMES;
40+
export const EDITABLE_BEHAVIOR_CORE_PROMPT_DOCUMENT_NAME_SET =
41+
EDITABLE_CORE_PROMPT_DOCUMENT_NAME_SET;
42+
3043
export const SKILL_PROMPT_NAME_REGEX = /^[a-z0-9][a-z0-9-]{1,62}\.md$/;
3144

3245
export class PromptDocumentValidationError extends Error {

apps/api/src/ai-agent/prompts/instructions.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ export function buildBehaviorInstructions(
2222
instructions.push(escalationInstructions);
2323
}
2424

25-
// Build capability list
26-
const capabilities = buildCapabilitiesInstructions(settings);
27-
if (capabilities) {
28-
instructions.push(capabilities);
29-
}
30-
3125
// Add mode-specific behavior
3226
const modeInstructions = buildModeBehaviorInstructions(mode);
3327
if (modeInstructions) {

0 commit comments

Comments
 (0)