@start-x-work/mos-creative 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.
@@ -0,0 +1,218 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * brief.json — the single starting point of every generation.
5
+ *
6
+ * Per CR1 §2-3, the "ad-design" school (start from what to assert, not from a
7
+ * free prompt) is the default: generation always originates from a Brief.
8
+ * Free-form prompt input is demoted to an auxiliary hint at the app layer.
9
+ */
10
+ /** Content format the brief targets. */
11
+ declare const FormatSchema: z.ZodEnum<{
12
+ ad_copy: "ad_copy";
13
+ lp_hero: "lp_hero";
14
+ sns_post: "sns_post";
15
+ email: "email";
16
+ article: "article";
17
+ banner: "banner";
18
+ storyboard: "storyboard";
19
+ }>;
20
+ type Format = z.infer<typeof FormatSchema>;
21
+ /** Industry guard set names shared with the guard dictionaries. */
22
+ declare const IndustryGuardSetSchema: z.ZodEnum<{
23
+ general: "general";
24
+ cosmetics: "cosmetics";
25
+ health_food: "health_food";
26
+ finance: "finance";
27
+ }>;
28
+ type IndustryGuardSet = z.infer<typeof IndustryGuardSetSchema>;
29
+ declare const ToneSchema: z.ZodObject<{
30
+ style: z.ZodDefault<z.ZodString>;
31
+ voice: z.ZodDefault<z.ZodString>;
32
+ ngWords: z.ZodDefault<z.ZodArray<z.ZodString>>;
33
+ }, z.core.$strip>;
34
+ type Tone = z.infer<typeof ToneSchema>;
35
+ declare const BriefSchema: z.ZodObject<{
36
+ version: z.ZodDefault<z.ZodLiteral<"1">>;
37
+ product: z.ZodObject<{
38
+ name: z.ZodDefault<z.ZodString>;
39
+ summary: z.ZodDefault<z.ZodString>;
40
+ url: z.ZodDefault<z.ZodString>;
41
+ }, z.core.$strip>;
42
+ audience: z.ZodObject<{
43
+ persona: z.ZodDefault<z.ZodString>;
44
+ journeyStage: z.ZodDefault<z.ZodString>;
45
+ }, z.core.$strip>;
46
+ kbf: z.ZodDefault<z.ZodArray<z.ZodString>>;
47
+ tone: z.ZodObject<{
48
+ style: z.ZodDefault<z.ZodString>;
49
+ voice: z.ZodDefault<z.ZodString>;
50
+ ngWords: z.ZodDefault<z.ZodArray<z.ZodString>>;
51
+ }, z.core.$strip>;
52
+ format: z.ZodEnum<{
53
+ ad_copy: "ad_copy";
54
+ lp_hero: "lp_hero";
55
+ sns_post: "sns_post";
56
+ email: "email";
57
+ article: "article";
58
+ banner: "banner";
59
+ storyboard: "storyboard";
60
+ }>;
61
+ constraints: z.ZodObject<{
62
+ maxLength: z.ZodDefault<z.ZodNumber>;
63
+ mustInclude: z.ZodDefault<z.ZodArray<z.ZodString>>;
64
+ industryGuardSet: z.ZodDefault<z.ZodEnum<{
65
+ general: "general";
66
+ cosmetics: "cosmetics";
67
+ health_food: "health_food";
68
+ finance: "finance";
69
+ }>>;
70
+ }, z.core.$strip>;
71
+ grounding: z.ZodObject<{
72
+ facts: z.ZodDefault<z.ZodArray<z.ZodString>>;
73
+ }, z.core.$strip>;
74
+ }, z.core.$strip>;
75
+ type Brief = z.infer<typeof BriefSchema>;
76
+ /** Parse + fill defaults. Throws ZodError on invalid input. */
77
+ declare function parseBrief(input: unknown): Brief;
78
+ /** A minimal, valid empty brief for a given format (used to seed the editor). */
79
+ declare function emptyBrief(format?: Format): Brief;
80
+
81
+ /** Status lifecycle. No path exists that reaches "approved" without a human. */
82
+ type CreativeStatus = "draft" | "approved" | "archived";
83
+ /** A single generated candidate (text-first in v0.1). */
84
+ interface Generation {
85
+ id: string;
86
+ briefId: string;
87
+ /** The generated text body (draft). */
88
+ text: string;
89
+ /** Which model produced it, for the trail (e.g. "gemini-2.x"). */
90
+ model: string;
91
+ /** ISO timestamp; supplied by the caller (core does not read the clock). */
92
+ createdAt: string;
93
+ }
94
+ /** Severity of a guard finding. Never "error" — the guard only advises. */
95
+ type GuardSeverity = "info" | "warning";
96
+ /** A guard finding: a highlighted span plus the point to check. NO rewrite. */
97
+ interface GuardFinding {
98
+ /** Rule id, e.g. "assertion", "superlative", "stealth_marketing". */
99
+ ruleId: string;
100
+ severity: GuardSeverity;
101
+ /** The matched substring in the reviewed text. */
102
+ match: string;
103
+ /** Character offset of the match within the reviewed text. */
104
+ index: number;
105
+ /** The point a human should check — an advisory, never a replacement string. */
106
+ point: string;
107
+ /** Which dictionary raised it, e.g. "general" or "cosmetics". */
108
+ set: string;
109
+ }
110
+ interface GuardReport {
111
+ findings: GuardFinding[];
112
+ /** True if AI-based contextual judging was requested and merged in. */
113
+ aiJudged: boolean;
114
+ }
115
+ /** Approval requires BOTH a reviewer name AND explicit flag acknowledgement. */
116
+ interface ApprovalTrail {
117
+ approver: string;
118
+ approvedAt: string;
119
+ /** The guard findings the approver acknowledged at approval time. */
120
+ acknowledgedFindings: GuardFinding[];
121
+ /** How many findings were shown; kept for auditability. */
122
+ findingCountAtApproval: number;
123
+ }
124
+ /** A creative unit: brief + chosen draft + (once approved) its trail. */
125
+ interface CreativeRecord {
126
+ id: string;
127
+ brief: Brief;
128
+ generation: Generation;
129
+ status: CreativeStatus;
130
+ guard?: GuardReport;
131
+ approval?: ApprovalTrail;
132
+ }
133
+
134
+ declare class ApprovalError extends Error {
135
+ }
136
+ interface ApproveInput {
137
+ approver: string;
138
+ /** Must be true: the human confirmed they reviewed the guard flags. */
139
+ flagsAcknowledged: boolean;
140
+ guard: GuardReport;
141
+ /** ISO timestamp supplied by the caller (core never reads the clock). */
142
+ approvedAt: string;
143
+ }
144
+ /**
145
+ * Build an approval trail. Throws unless BOTH a non-empty approver name and an
146
+ * explicit flag acknowledgement are present. There is no code path that yields
147
+ * an ApprovalTrail without a human name — i.e. no unattended approval.
148
+ */
149
+ declare function buildApproval(input: ApproveInput): ApprovalTrail;
150
+ /** Transition a record to "approved" by attaching a valid trail. */
151
+ declare function approveRecord(record: CreativeRecord, input: ApproveInput): CreativeRecord;
152
+
153
+ declare function toMarkdown(record: CreativeRecord): string;
154
+ /** One row per record. Trail columns are always present. */
155
+ declare function toCsv(records: CreativeRecord[]): string;
156
+ interface MarketingOsExport {
157
+ schema: string;
158
+ brief: CreativeRecord["brief"];
159
+ generation: CreativeRecord["generation"];
160
+ status: CreativeRecord["status"];
161
+ guard: CreativeRecord["guard"];
162
+ approval: CreativeRecord["approval"];
163
+ }
164
+ /** Single-source interchange JSON for the Marketing-OS body O2 (D-class) intake. */
165
+ declare function toMarketingOsJson(record: CreativeRecord): MarketingOsExport;
166
+ interface BackupFile {
167
+ schema: string;
168
+ records: CreativeRecord[];
169
+ }
170
+ /** Whole-project backup; importable for full restore. */
171
+ declare function toBackup(records: CreativeRecord[]): BackupFile;
172
+ declare function fromBackup(data: unknown): CreativeRecord[];
173
+
174
+ /**
175
+ * The expression guard. It is deliberately limited to "highlight + point to
176
+ * check": it only flags a span and explains what to verify. It never edits the
177
+ * text and never proposes alternative wording for the user — the human edits in
178
+ * the editor. This is a design constraint (CR1 §2-4, D-7 patent-risk avoidance),
179
+ * not an omission. A GuardFinding has no suggestion/alternative field.
180
+ */
181
+ interface GuardRule {
182
+ id: string;
183
+ severity: GuardSeverity;
184
+ point: string;
185
+ /** Any occurrence of these terms raises a finding at each match. */
186
+ terms?: string[];
187
+ /** If NONE of these markers are present, raise a single finding. */
188
+ requireOneOf?: string[];
189
+ }
190
+ interface GuardRuleSet {
191
+ set: string;
192
+ note?: string;
193
+ rules: GuardRule[];
194
+ }
195
+ declare const BUILTIN_RULE_SETS: Record<string, GuardRuleSet>;
196
+ /** Resolve the rule sets to apply: general is always included. */
197
+ declare function resolveRuleSets(industryGuardSet: string, extra?: GuardRuleSet[]): GuardRuleSet[];
198
+ /**
199
+ * Run the deterministic guard over `text`.
200
+ * @param userNgWords brief.tone.ngWords — user-defined NG words, always warned.
201
+ * @param aiFindings optional findings from an opt-in AI pass (app layer). Merged
202
+ * in as-is; core never calls a network itself.
203
+ */
204
+ declare function runGuard(text: string, sets: GuardRuleSet[], userNgWords?: string[], aiFindings?: GuardFinding[]): GuardReport;
205
+
206
+ /**
207
+ * Prompt scaffolds. Pure functions that turn a Brief into a prompt string.
208
+ *
209
+ * Written fresh for this OSS (no carry-over of private-repo prompt text, per
210
+ * §0 audit decision). The grounding rule (§2-3) is embedded in every prompt so
211
+ * the model is structurally discouraged from inventing numbers.
212
+ */
213
+ /** The grounding + boundary preamble shared by all formats. */
214
+ declare function groundingPreamble(brief: Brief): string;
215
+ /** Build the full prompt for a brief's format. */
216
+ declare function buildPrompt(brief: Brief): string;
217
+
218
+ export { ApprovalError, type ApprovalTrail, type ApproveInput, BUILTIN_RULE_SETS, type BackupFile, type Brief, BriefSchema, type CreativeRecord, type CreativeStatus, type Format, FormatSchema, type Generation, type GuardFinding, type GuardReport, type GuardRule, type GuardRuleSet, type GuardSeverity, type IndustryGuardSet, IndustryGuardSetSchema, type MarketingOsExport, type Tone, ToneSchema, approveRecord, buildApproval, buildPrompt, emptyBrief, fromBackup, groundingPreamble, parseBrief, resolveRuleSets, runGuard, toBackup, toCsv, toMarkdown, toMarketingOsJson };
@@ -0,0 +1,218 @@
1
+ import { z } from 'zod';
2
+
3
+ /**
4
+ * brief.json — the single starting point of every generation.
5
+ *
6
+ * Per CR1 §2-3, the "ad-design" school (start from what to assert, not from a
7
+ * free prompt) is the default: generation always originates from a Brief.
8
+ * Free-form prompt input is demoted to an auxiliary hint at the app layer.
9
+ */
10
+ /** Content format the brief targets. */
11
+ declare const FormatSchema: z.ZodEnum<{
12
+ ad_copy: "ad_copy";
13
+ lp_hero: "lp_hero";
14
+ sns_post: "sns_post";
15
+ email: "email";
16
+ article: "article";
17
+ banner: "banner";
18
+ storyboard: "storyboard";
19
+ }>;
20
+ type Format = z.infer<typeof FormatSchema>;
21
+ /** Industry guard set names shared with the guard dictionaries. */
22
+ declare const IndustryGuardSetSchema: z.ZodEnum<{
23
+ general: "general";
24
+ cosmetics: "cosmetics";
25
+ health_food: "health_food";
26
+ finance: "finance";
27
+ }>;
28
+ type IndustryGuardSet = z.infer<typeof IndustryGuardSetSchema>;
29
+ declare const ToneSchema: z.ZodObject<{
30
+ style: z.ZodDefault<z.ZodString>;
31
+ voice: z.ZodDefault<z.ZodString>;
32
+ ngWords: z.ZodDefault<z.ZodArray<z.ZodString>>;
33
+ }, z.core.$strip>;
34
+ type Tone = z.infer<typeof ToneSchema>;
35
+ declare const BriefSchema: z.ZodObject<{
36
+ version: z.ZodDefault<z.ZodLiteral<"1">>;
37
+ product: z.ZodObject<{
38
+ name: z.ZodDefault<z.ZodString>;
39
+ summary: z.ZodDefault<z.ZodString>;
40
+ url: z.ZodDefault<z.ZodString>;
41
+ }, z.core.$strip>;
42
+ audience: z.ZodObject<{
43
+ persona: z.ZodDefault<z.ZodString>;
44
+ journeyStage: z.ZodDefault<z.ZodString>;
45
+ }, z.core.$strip>;
46
+ kbf: z.ZodDefault<z.ZodArray<z.ZodString>>;
47
+ tone: z.ZodObject<{
48
+ style: z.ZodDefault<z.ZodString>;
49
+ voice: z.ZodDefault<z.ZodString>;
50
+ ngWords: z.ZodDefault<z.ZodArray<z.ZodString>>;
51
+ }, z.core.$strip>;
52
+ format: z.ZodEnum<{
53
+ ad_copy: "ad_copy";
54
+ lp_hero: "lp_hero";
55
+ sns_post: "sns_post";
56
+ email: "email";
57
+ article: "article";
58
+ banner: "banner";
59
+ storyboard: "storyboard";
60
+ }>;
61
+ constraints: z.ZodObject<{
62
+ maxLength: z.ZodDefault<z.ZodNumber>;
63
+ mustInclude: z.ZodDefault<z.ZodArray<z.ZodString>>;
64
+ industryGuardSet: z.ZodDefault<z.ZodEnum<{
65
+ general: "general";
66
+ cosmetics: "cosmetics";
67
+ health_food: "health_food";
68
+ finance: "finance";
69
+ }>>;
70
+ }, z.core.$strip>;
71
+ grounding: z.ZodObject<{
72
+ facts: z.ZodDefault<z.ZodArray<z.ZodString>>;
73
+ }, z.core.$strip>;
74
+ }, z.core.$strip>;
75
+ type Brief = z.infer<typeof BriefSchema>;
76
+ /** Parse + fill defaults. Throws ZodError on invalid input. */
77
+ declare function parseBrief(input: unknown): Brief;
78
+ /** A minimal, valid empty brief for a given format (used to seed the editor). */
79
+ declare function emptyBrief(format?: Format): Brief;
80
+
81
+ /** Status lifecycle. No path exists that reaches "approved" without a human. */
82
+ type CreativeStatus = "draft" | "approved" | "archived";
83
+ /** A single generated candidate (text-first in v0.1). */
84
+ interface Generation {
85
+ id: string;
86
+ briefId: string;
87
+ /** The generated text body (draft). */
88
+ text: string;
89
+ /** Which model produced it, for the trail (e.g. "gemini-2.x"). */
90
+ model: string;
91
+ /** ISO timestamp; supplied by the caller (core does not read the clock). */
92
+ createdAt: string;
93
+ }
94
+ /** Severity of a guard finding. Never "error" — the guard only advises. */
95
+ type GuardSeverity = "info" | "warning";
96
+ /** A guard finding: a highlighted span plus the point to check. NO rewrite. */
97
+ interface GuardFinding {
98
+ /** Rule id, e.g. "assertion", "superlative", "stealth_marketing". */
99
+ ruleId: string;
100
+ severity: GuardSeverity;
101
+ /** The matched substring in the reviewed text. */
102
+ match: string;
103
+ /** Character offset of the match within the reviewed text. */
104
+ index: number;
105
+ /** The point a human should check — an advisory, never a replacement string. */
106
+ point: string;
107
+ /** Which dictionary raised it, e.g. "general" or "cosmetics". */
108
+ set: string;
109
+ }
110
+ interface GuardReport {
111
+ findings: GuardFinding[];
112
+ /** True if AI-based contextual judging was requested and merged in. */
113
+ aiJudged: boolean;
114
+ }
115
+ /** Approval requires BOTH a reviewer name AND explicit flag acknowledgement. */
116
+ interface ApprovalTrail {
117
+ approver: string;
118
+ approvedAt: string;
119
+ /** The guard findings the approver acknowledged at approval time. */
120
+ acknowledgedFindings: GuardFinding[];
121
+ /** How many findings were shown; kept for auditability. */
122
+ findingCountAtApproval: number;
123
+ }
124
+ /** A creative unit: brief + chosen draft + (once approved) its trail. */
125
+ interface CreativeRecord {
126
+ id: string;
127
+ brief: Brief;
128
+ generation: Generation;
129
+ status: CreativeStatus;
130
+ guard?: GuardReport;
131
+ approval?: ApprovalTrail;
132
+ }
133
+
134
+ declare class ApprovalError extends Error {
135
+ }
136
+ interface ApproveInput {
137
+ approver: string;
138
+ /** Must be true: the human confirmed they reviewed the guard flags. */
139
+ flagsAcknowledged: boolean;
140
+ guard: GuardReport;
141
+ /** ISO timestamp supplied by the caller (core never reads the clock). */
142
+ approvedAt: string;
143
+ }
144
+ /**
145
+ * Build an approval trail. Throws unless BOTH a non-empty approver name and an
146
+ * explicit flag acknowledgement are present. There is no code path that yields
147
+ * an ApprovalTrail without a human name — i.e. no unattended approval.
148
+ */
149
+ declare function buildApproval(input: ApproveInput): ApprovalTrail;
150
+ /** Transition a record to "approved" by attaching a valid trail. */
151
+ declare function approveRecord(record: CreativeRecord, input: ApproveInput): CreativeRecord;
152
+
153
+ declare function toMarkdown(record: CreativeRecord): string;
154
+ /** One row per record. Trail columns are always present. */
155
+ declare function toCsv(records: CreativeRecord[]): string;
156
+ interface MarketingOsExport {
157
+ schema: string;
158
+ brief: CreativeRecord["brief"];
159
+ generation: CreativeRecord["generation"];
160
+ status: CreativeRecord["status"];
161
+ guard: CreativeRecord["guard"];
162
+ approval: CreativeRecord["approval"];
163
+ }
164
+ /** Single-source interchange JSON for the Marketing-OS body O2 (D-class) intake. */
165
+ declare function toMarketingOsJson(record: CreativeRecord): MarketingOsExport;
166
+ interface BackupFile {
167
+ schema: string;
168
+ records: CreativeRecord[];
169
+ }
170
+ /** Whole-project backup; importable for full restore. */
171
+ declare function toBackup(records: CreativeRecord[]): BackupFile;
172
+ declare function fromBackup(data: unknown): CreativeRecord[];
173
+
174
+ /**
175
+ * The expression guard. It is deliberately limited to "highlight + point to
176
+ * check": it only flags a span and explains what to verify. It never edits the
177
+ * text and never proposes alternative wording for the user — the human edits in
178
+ * the editor. This is a design constraint (CR1 §2-4, D-7 patent-risk avoidance),
179
+ * not an omission. A GuardFinding has no suggestion/alternative field.
180
+ */
181
+ interface GuardRule {
182
+ id: string;
183
+ severity: GuardSeverity;
184
+ point: string;
185
+ /** Any occurrence of these terms raises a finding at each match. */
186
+ terms?: string[];
187
+ /** If NONE of these markers are present, raise a single finding. */
188
+ requireOneOf?: string[];
189
+ }
190
+ interface GuardRuleSet {
191
+ set: string;
192
+ note?: string;
193
+ rules: GuardRule[];
194
+ }
195
+ declare const BUILTIN_RULE_SETS: Record<string, GuardRuleSet>;
196
+ /** Resolve the rule sets to apply: general is always included. */
197
+ declare function resolveRuleSets(industryGuardSet: string, extra?: GuardRuleSet[]): GuardRuleSet[];
198
+ /**
199
+ * Run the deterministic guard over `text`.
200
+ * @param userNgWords brief.tone.ngWords — user-defined NG words, always warned.
201
+ * @param aiFindings optional findings from an opt-in AI pass (app layer). Merged
202
+ * in as-is; core never calls a network itself.
203
+ */
204
+ declare function runGuard(text: string, sets: GuardRuleSet[], userNgWords?: string[], aiFindings?: GuardFinding[]): GuardReport;
205
+
206
+ /**
207
+ * Prompt scaffolds. Pure functions that turn a Brief into a prompt string.
208
+ *
209
+ * Written fresh for this OSS (no carry-over of private-repo prompt text, per
210
+ * §0 audit decision). The grounding rule (§2-3) is embedded in every prompt so
211
+ * the model is structurally discouraged from inventing numbers.
212
+ */
213
+ /** The grounding + boundary preamble shared by all formats. */
214
+ declare function groundingPreamble(brief: Brief): string;
215
+ /** Build the full prompt for a brief's format. */
216
+ declare function buildPrompt(brief: Brief): string;
217
+
218
+ export { ApprovalError, type ApprovalTrail, type ApproveInput, BUILTIN_RULE_SETS, type BackupFile, type Brief, BriefSchema, type CreativeRecord, type CreativeStatus, type Format, FormatSchema, type Generation, type GuardFinding, type GuardReport, type GuardRule, type GuardRuleSet, type GuardSeverity, type IndustryGuardSet, IndustryGuardSetSchema, type MarketingOsExport, type Tone, ToneSchema, approveRecord, buildApproval, buildPrompt, emptyBrief, fromBackup, groundingPreamble, parseBrief, resolveRuleSets, runGuard, toBackup, toCsv, toMarkdown, toMarketingOsJson };