@reallyartificial/grain 0.2.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.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ import { Grain } from "./grain.js";
3
+ const args = process.argv.slice(2);
4
+ const command = args[0];
5
+ function printHelp() {
6
+ console.log(`grain — CLI for Grain agent definitions
7
+
8
+ Usage:
9
+ grain validate <file> Validate a .agent.yaml file
10
+ grain generate <file> [--channel] Generate system prompt
11
+ grain info <file> Show agent summary
12
+
13
+ Examples:
14
+ grain validate my-agent.agent.yaml
15
+ grain generate my-agent.agent.yaml --channel slack
16
+ grain info my-agent.agent.yaml`);
17
+ }
18
+ function validate(file) {
19
+ try {
20
+ const g = Grain.load(file);
21
+ const errors = g.validate();
22
+ if (errors.length > 0) {
23
+ console.error(`Validation warnings for ${file}:`);
24
+ for (const err of errors) {
25
+ console.error(` ${err.path}: ${err.message}`);
26
+ }
27
+ process.exit(1);
28
+ }
29
+ console.log(`${file} is valid`);
30
+ }
31
+ catch (e) {
32
+ console.error(e instanceof Error ? e.message : String(e));
33
+ process.exit(1);
34
+ }
35
+ }
36
+ function generate(file, channel) {
37
+ try {
38
+ const g = Grain.load(file);
39
+ const prompt = g.toPrompt(channel);
40
+ console.log(prompt);
41
+ }
42
+ catch (e) {
43
+ console.error(e instanceof Error ? e.message : String(e));
44
+ process.exit(1);
45
+ }
46
+ }
47
+ function info(file) {
48
+ try {
49
+ const g = Grain.load(file);
50
+ console.log(`Name: ${g.name}`);
51
+ console.log(`ID: ${g.id}`);
52
+ console.log(`Version: ${g.version}`);
53
+ console.log(`Description: ${g.data.meta.description}`);
54
+ console.log(`Role: ${g.data.identity.role}`);
55
+ if (g.data.voice.channelOverrides) {
56
+ const channels = Object.keys(g.data.voice.channelOverrides);
57
+ console.log(`Channels: ${channels.join(", ")}`);
58
+ }
59
+ const p = g.personality;
60
+ const traits = [];
61
+ if (p.formality > 0.7)
62
+ traits.push("formal");
63
+ else if (p.formality < 0.3)
64
+ traits.push("casual");
65
+ if (p.warmth > 0.7)
66
+ traits.push("warm");
67
+ if (p.humor > 0.5)
68
+ traits.push("humorous");
69
+ if (p.assertiveness > 0.7)
70
+ traits.push("assertive");
71
+ if (p.confidence > 0.7)
72
+ traits.push("confident");
73
+ if (p.concreteness > 0.7)
74
+ traits.push("concrete");
75
+ if (traits.length) {
76
+ console.log(`Personality: ${traits.join(", ")}`);
77
+ }
78
+ console.log(`Rules: ${g.rules.length}`);
79
+ console.log(`Boundaries: ${g.boundaries.length}`);
80
+ if (g.tools.length) {
81
+ console.log(`Tools: ${g.tools.map(t => t.id).join(", ")}`);
82
+ }
83
+ }
84
+ catch (e) {
85
+ console.error(e instanceof Error ? e.message : String(e));
86
+ process.exit(1);
87
+ }
88
+ }
89
+ if (!command || command === "--help" || command === "-h") {
90
+ printHelp();
91
+ process.exit(0);
92
+ }
93
+ const file = args[1];
94
+ if (!file) {
95
+ console.error(`Error: missing file argument\n`);
96
+ printHelp();
97
+ process.exit(1);
98
+ }
99
+ switch (command) {
100
+ case "validate":
101
+ validate(file);
102
+ break;
103
+ case "generate": {
104
+ const channelIdx = args.indexOf("--channel");
105
+ const channel = channelIdx !== -1 ? args[channelIdx + 1] : undefined;
106
+ generate(file, channel);
107
+ break;
108
+ }
109
+ case "info":
110
+ info(file);
111
+ break;
112
+ default:
113
+ console.error(`Unknown command: ${command}\n`);
114
+ printHelp();
115
+ process.exit(1);
116
+ }
117
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAA;AAElC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAClC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;AAEvB,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;iCAUmB,CAAC,CAAA;AAClC,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1B,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAA;QAC3B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,2BAA2B,IAAI,GAAG,CAAC,CAAA;YACjD,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;gBACzB,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;YAChD,CAAC;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,WAAW,CAAC,CAAA;IACjC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,OAAgB;IAC9C,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1B,MAAM,MAAM,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QAClC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IACrB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,SAAS,IAAI,CAAC,IAAY;IACxB,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC1B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;QACrC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;QACnC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;QACxC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;QACtD,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;QAEnD,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAA;YAC3D,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACpD,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,CAAA;QACvB,MAAM,MAAM,GAAa,EAAE,CAAA;QAC3B,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;aACvC,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACjD,IAAI,CAAC,CAAC,MAAM,GAAG,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACvC,IAAI,CAAC,CAAC,KAAK,GAAG,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAC1C,IAAI,CAAC,CAAC,aAAa,GAAG,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QACnD,IAAI,CAAC,CAAC,UAAU,GAAG,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAChD,IAAI,CAAC,CAAC,YAAY,GAAG,GAAG;YAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QACjD,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAClD,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAA;QAC7C,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;QAClD,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACnB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAClE,CAAC;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;AACH,CAAC;AAED,IAAI,CAAC,OAAO,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;IACzD,SAAS,EAAE,CAAA;IACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC;AAED,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,CAAA;AACpB,IAAI,CAAC,IAAI,EAAE,CAAC;IACV,OAAO,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAA;IAC/C,SAAS,EAAE,CAAA;IACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC;AAED,QAAQ,OAAO,EAAE,CAAC;IAChB,KAAK,UAAU;QACb,QAAQ,CAAC,IAAI,CAAC,CAAA;QACd,MAAK;IACP,KAAK,UAAU,CAAC,CAAC,CAAC;QAChB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAC5C,MAAM,OAAO,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QACpE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACvB,MAAK;IACP,CAAC;IACD,KAAK,MAAM;QACT,IAAI,CAAC,IAAI,CAAC,CAAA;QACV,MAAK;IACP;QACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,IAAI,CAAC,CAAA;QAC9C,SAAS,EAAE,CAAA;QACX,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACnB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { AgentSpec } from "./types.js";
2
+ /**
3
+ * Fills in missing sections with sensible defaults.
4
+ * Allows minimal specs (just specVersion, id, version, meta) to work
5
+ * by providing neutral defaults for all other sections.
6
+ */
7
+ export declare function resolveDefaults(partial: Record<string, unknown>): AgentSpec;
8
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAwH3E"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Fills in missing sections with sensible defaults.
3
+ * Allows minimal specs (just specVersion, id, version, meta) to work
4
+ * by providing neutral defaults for all other sections.
5
+ */
6
+ export function resolveDefaults(partial) {
7
+ const spec = { ...partial };
8
+ if (!spec.identity) {
9
+ const meta = spec.meta;
10
+ spec.identity = {
11
+ name: meta.name,
12
+ role: "AI Assistant",
13
+ purpose: { primary: meta.description || "Assist the user" },
14
+ expertise: [],
15
+ };
16
+ }
17
+ if (!spec.voice) {
18
+ spec.voice = {
19
+ personality: {
20
+ formality: 0.5,
21
+ warmth: 0.5,
22
+ humor: 0.3,
23
+ assertiveness: 0.5,
24
+ verbosity: 0.5,
25
+ confidence: 0.6,
26
+ concreteness: 0.6,
27
+ urgency: 0.4,
28
+ },
29
+ language: {
30
+ locale: "en",
31
+ sentenceLength: "medium",
32
+ jargonLevel: "moderate",
33
+ emojiUsage: "never",
34
+ structure: "mixed",
35
+ usesAnalogies: false,
36
+ addressing: "first-person",
37
+ },
38
+ };
39
+ }
40
+ if (!spec.cognition) {
41
+ spec.cognition = {
42
+ reasoningStyle: {
43
+ primary: "analytical",
44
+ showReasoning: false,
45
+ analysisDepth: "moderate",
46
+ multiPerspective: false,
47
+ },
48
+ decisionMaking: {
49
+ speed: 0.5,
50
+ evidenceThreshold: 0.5,
51
+ reversibilityPreference: "neutral",
52
+ autonomy: "ask-for-major",
53
+ },
54
+ uncertainty: {
55
+ lowConfidenceAction: "ask",
56
+ confidenceFloor: 0.3,
57
+ communicateUncertainty: true,
58
+ conflictResolution: "ask-user",
59
+ },
60
+ taskStrategy: {
61
+ decomposition: "top-down",
62
+ maxParallelism: 1,
63
+ checkpointing: false,
64
+ blockingStrategy: "escalate",
65
+ },
66
+ };
67
+ }
68
+ if (!spec.capabilities) {
69
+ spec.capabilities = {};
70
+ }
71
+ if (!spec.behavior) {
72
+ spec.behavior = { rules: [], boundaries: [] };
73
+ }
74
+ if (!spec.memory) {
75
+ spec.memory = {
76
+ retention: [{ category: "conversation-history", duration: "session" }],
77
+ contextStrategy: {
78
+ overflow: "summarize",
79
+ prioritize: ["recent"],
80
+ autoSummarize: false,
81
+ },
82
+ };
83
+ }
84
+ if (!spec.communication) {
85
+ spec.communication = {
86
+ channels: [{ channel: "api", enabled: true }],
87
+ input: {
88
+ languages: ["en"],
89
+ typoTolerance: true,
90
+ ambiguityResolution: "ask",
91
+ modalities: ["text"],
92
+ },
93
+ output: {
94
+ defaultFormat: "text",
95
+ citations: "never",
96
+ structuredOutput: false,
97
+ },
98
+ };
99
+ }
100
+ if (!spec.adaptation) {
101
+ spec.adaptation = { enabled: false };
102
+ }
103
+ if (!spec.observability) {
104
+ spec.observability = {
105
+ logging: {
106
+ level: "info",
107
+ alwaysLog: ["error"],
108
+ conversationLogging: "none",
109
+ piiHandling: "redact",
110
+ },
111
+ metrics: [],
112
+ successCriteria: [],
113
+ };
114
+ }
115
+ return spec;
116
+ }
117
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,78 @@
1
+ import type { AgentSpec, PersonalityVector, BehaviorRule, Boundary, ToolCapability, SkillDefinition, DomainExpertise } from "./types.js";
2
+ import { type ValidationError } from "./validate.js";
3
+ /**
4
+ * Grain — immutable wrapper around an AgentSpec.
5
+ * Every mutation returns a new Grain; the original is never modified.
6
+ */
7
+ export declare class Grain {
8
+ private readonly _data;
9
+ private constructor();
10
+ /** Create a Grain programmatically with an id and optional overrides. */
11
+ static create(id: string, overrides?: {
12
+ name?: string;
13
+ description?: string;
14
+ }): Grain;
15
+ /** Create a Grain from a YAML or JSON string. */
16
+ static from(content: string): Grain;
17
+ /** Load a Grain from a .agent.yaml or .agent.json file. */
18
+ static load(filePath: string): Grain;
19
+ /** Create a Grain from a plain AgentSpec object. */
20
+ static of(spec: AgentSpec): Grain;
21
+ private with;
22
+ get id(): string;
23
+ get name(): string;
24
+ get version(): string;
25
+ get personality(): PersonalityVector;
26
+ get rules(): BehaviorRule[];
27
+ get boundaries(): Boundary[];
28
+ get tools(): ToolCapability[];
29
+ get skills(): SkillDefinition[];
30
+ get expertise(): DomainExpertise[];
31
+ get data(): AgentSpec;
32
+ get isValid(): boolean;
33
+ hasRule(id: string): boolean;
34
+ hasTool(name: string): boolean;
35
+ hasSkill(name: string): boolean;
36
+ addRule(rule: BehaviorRule): Grain;
37
+ removeRule(id: string): Grain;
38
+ addRules(rules: BehaviorRule[]): Grain;
39
+ addBoundary(boundary: Boundary): Grain;
40
+ removeBoundary(description: string): Grain;
41
+ addBoundaries(boundaries: Boundary[]): Grain;
42
+ addTool(tool: ToolCapability): Grain;
43
+ removeTool(name: string): Grain;
44
+ addTools(tools: ToolCapability[]): Grain;
45
+ addSkill(skill: SkillDefinition): Grain;
46
+ removeSkill(name: string): Grain;
47
+ addSkills(skills: SkillDefinition[]): Grain;
48
+ addExpertise(domain: string, proficiency: number): Grain;
49
+ removeExpertise(domain: string): Grain;
50
+ setPersonality(dim: keyof PersonalityVector, value: number): Grain;
51
+ set(path: string, value: unknown): Grain;
52
+ get<T = unknown>(path: string): T;
53
+ merge(other: Grain): Grain;
54
+ /**
55
+ * Clean YAML for LLM consumption.
56
+ * Strips SDK metadata (specVersion, id, version, meta.model, meta.tags,
57
+ * observability, adaptation). Keeps agent-facing content.
58
+ * Applies channel overrides if specified.
59
+ */
60
+ toString(channel?: string): string;
61
+ /**
62
+ * Expanded natural language prompt with behavioral directives.
63
+ * Delegates to the existing prompt generator.
64
+ */
65
+ toPrompt(channel?: string): string;
66
+ /** Full YAML including all metadata (for saving to file). */
67
+ toYAML(): string;
68
+ /** Full JSON including all metadata. */
69
+ toJSON(): string;
70
+ /** Validate and return errors. */
71
+ validate(): ValidationError[];
72
+ /** Diff this Grain against another. Returns paths with before/after values. */
73
+ diff(other: Grain): Record<string, {
74
+ before: unknown;
75
+ after: unknown;
76
+ }>;
77
+ }
78
+ //# sourceMappingURL=grain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grain.d.ts","sourceRoot":"","sources":["../src/grain.ts"],"names":[],"mappings":"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"}
package/dist/grain.js ADDED
@@ -0,0 +1,317 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
3
+ import { validateSpec, assertValid } from "./validate.js";
4
+ import { resolveDefaults } from "./defaults.js";
5
+ import { generateSystemPrompt } from "./prompt-generator.js";
6
+ /**
7
+ * Grain — immutable wrapper around an AgentSpec.
8
+ * Every mutation returns a new Grain; the original is never modified.
9
+ */
10
+ export class Grain {
11
+ _data;
12
+ constructor(data) {
13
+ this._data = Object.freeze(data);
14
+ }
15
+ // ===========================================================================
16
+ // CONSTRUCTORS
17
+ // ===========================================================================
18
+ /** Create a Grain programmatically with an id and optional overrides. */
19
+ static create(id, overrides = {}) {
20
+ const raw = {
21
+ specVersion: "1.0",
22
+ id,
23
+ version: "1.0.0",
24
+ meta: {
25
+ name: overrides.name ?? id,
26
+ description: overrides.description ?? "",
27
+ },
28
+ };
29
+ const spec = resolveDefaults(raw);
30
+ assertValid(spec);
31
+ return new Grain(spec);
32
+ }
33
+ /** Create a Grain from a YAML or JSON string. */
34
+ static from(content) {
35
+ let data;
36
+ try {
37
+ data = JSON.parse(content);
38
+ }
39
+ catch {
40
+ data = parseYaml(content);
41
+ }
42
+ assertValid(data);
43
+ return new Grain(resolveDefaults(data));
44
+ }
45
+ /** Load a Grain from a .agent.yaml or .agent.json file. */
46
+ static load(filePath) {
47
+ const content = readFileSync(filePath, "utf-8");
48
+ let data;
49
+ if (filePath.endsWith(".json")) {
50
+ data = JSON.parse(content);
51
+ }
52
+ else {
53
+ data = parseYaml(content);
54
+ }
55
+ assertValid(data);
56
+ return new Grain(resolveDefaults(data));
57
+ }
58
+ /** Create a Grain from a plain AgentSpec object. */
59
+ static of(spec) {
60
+ assertValid(spec);
61
+ return new Grain(resolveDefaults(spec));
62
+ }
63
+ // ===========================================================================
64
+ // INTERNAL HELPER
65
+ // ===========================================================================
66
+ with(updater) {
67
+ const clone = structuredClone(this._data);
68
+ updater(clone);
69
+ return new Grain(clone);
70
+ }
71
+ // ===========================================================================
72
+ // READ ACCESSORS
73
+ // ===========================================================================
74
+ get id() { return this._data.id; }
75
+ get name() { return this._data.meta.name; }
76
+ get version() { return this._data.version; }
77
+ get personality() {
78
+ return { ...this._data.voice.personality };
79
+ }
80
+ get rules() {
81
+ return structuredClone(this._data.behavior.rules);
82
+ }
83
+ get boundaries() {
84
+ return structuredClone(this._data.behavior.boundaries);
85
+ }
86
+ get tools() {
87
+ return structuredClone(this._data.capabilities.tools ?? []);
88
+ }
89
+ get skills() {
90
+ return structuredClone(this._data.capabilities.skills ?? []);
91
+ }
92
+ get expertise() {
93
+ return structuredClone(this._data.identity.expertise);
94
+ }
95
+ get data() {
96
+ return this._data;
97
+ }
98
+ get isValid() {
99
+ return validateSpec(this._data).length === 0;
100
+ }
101
+ // ===========================================================================
102
+ // QUERY
103
+ // ===========================================================================
104
+ hasRule(id) {
105
+ return this._data.behavior.rules.some(r => r.id === id);
106
+ }
107
+ hasTool(name) {
108
+ return (this._data.capabilities.tools ?? []).some(t => t.id === name);
109
+ }
110
+ hasSkill(name) {
111
+ return (this._data.capabilities.skills ?? []).some(s => s.name === name);
112
+ }
113
+ // ===========================================================================
114
+ // MUTATIONS (all return new Grain)
115
+ // ===========================================================================
116
+ addRule(rule) {
117
+ return this.with(d => { d.behavior.rules.push(rule); });
118
+ }
119
+ removeRule(id) {
120
+ return this.with(d => { d.behavior.rules = d.behavior.rules.filter(r => r.id !== id); });
121
+ }
122
+ addRules(rules) {
123
+ return this.with(d => { d.behavior.rules.push(...rules); });
124
+ }
125
+ addBoundary(boundary) {
126
+ return this.with(d => { d.behavior.boundaries.push(boundary); });
127
+ }
128
+ removeBoundary(description) {
129
+ return this.with(d => {
130
+ d.behavior.boundaries = d.behavior.boundaries.filter(b => b.description !== description);
131
+ });
132
+ }
133
+ addBoundaries(boundaries) {
134
+ return this.with(d => { d.behavior.boundaries.push(...boundaries); });
135
+ }
136
+ addTool(tool) {
137
+ return this.with(d => {
138
+ if (!d.capabilities.tools)
139
+ d.capabilities.tools = [];
140
+ d.capabilities.tools.push(tool);
141
+ });
142
+ }
143
+ removeTool(name) {
144
+ return this.with(d => {
145
+ d.capabilities.tools = (d.capabilities.tools ?? []).filter(t => t.id !== name);
146
+ });
147
+ }
148
+ addTools(tools) {
149
+ return this.with(d => {
150
+ if (!d.capabilities.tools)
151
+ d.capabilities.tools = [];
152
+ d.capabilities.tools.push(...tools);
153
+ });
154
+ }
155
+ addSkill(skill) {
156
+ return this.with(d => {
157
+ if (!d.capabilities.skills)
158
+ d.capabilities.skills = [];
159
+ d.capabilities.skills.push(skill);
160
+ });
161
+ }
162
+ removeSkill(name) {
163
+ return this.with(d => {
164
+ d.capabilities.skills = (d.capabilities.skills ?? []).filter(s => s.name !== name);
165
+ });
166
+ }
167
+ addSkills(skills) {
168
+ return this.with(d => {
169
+ if (!d.capabilities.skills)
170
+ d.capabilities.skills = [];
171
+ d.capabilities.skills.push(...skills);
172
+ });
173
+ }
174
+ addExpertise(domain, proficiency) {
175
+ if (proficiency < 0 || proficiency > 1) {
176
+ throw new RangeError(`Proficiency must be between 0 and 1, got ${proficiency}`);
177
+ }
178
+ return this.with(d => {
179
+ d.identity.expertise.push({ domain, proficiency });
180
+ });
181
+ }
182
+ removeExpertise(domain) {
183
+ return this.with(d => {
184
+ d.identity.expertise = d.identity.expertise.filter(e => e.domain !== domain);
185
+ });
186
+ }
187
+ setPersonality(dim, value) {
188
+ if (value < 0 || value > 1) {
189
+ throw new RangeError(`Personality value must be between 0 and 1, got ${value} for ${dim}`);
190
+ }
191
+ return this.with(d => {
192
+ d.voice.personality[dim] = value;
193
+ });
194
+ }
195
+ set(path, value) {
196
+ return this.with(d => {
197
+ const parts = path.split(".");
198
+ let obj = d;
199
+ for (let i = 0; i < parts.length - 1; i++) {
200
+ obj = obj[parts[i]];
201
+ }
202
+ obj[parts[parts.length - 1]] = value;
203
+ });
204
+ }
205
+ get(path) {
206
+ const parts = path.split(".");
207
+ let obj = this._data;
208
+ for (const part of parts) {
209
+ obj = obj[part];
210
+ }
211
+ return obj;
212
+ }
213
+ merge(other) {
214
+ return this.with(d => {
215
+ deepMerge(d, other._data);
216
+ });
217
+ }
218
+ // ===========================================================================
219
+ // OUTPUT
220
+ // ===========================================================================
221
+ /**
222
+ * Clean YAML for LLM consumption.
223
+ * Strips SDK metadata (specVersion, id, version, meta.model, meta.tags,
224
+ * observability, adaptation). Keeps agent-facing content.
225
+ * Applies channel overrides if specified.
226
+ */
227
+ toString(channel) {
228
+ const clone = structuredClone(this._data);
229
+ // Apply channel overrides to voice
230
+ if (channel) {
231
+ const voice = clone.voice;
232
+ const overrides = voice.channelOverrides?.[channel];
233
+ if (overrides) {
234
+ const personality = voice.personality;
235
+ const language = voice.language;
236
+ Object.assign(personality, overrides);
237
+ Object.assign(language, overrides);
238
+ }
239
+ }
240
+ // Strip SDK metadata
241
+ delete clone.specVersion;
242
+ delete clone.id;
243
+ delete clone.version;
244
+ delete clone.observability;
245
+ delete clone.adaptation;
246
+ const meta = clone.meta;
247
+ delete meta.model;
248
+ delete meta.tags;
249
+ // Remove channelOverrides from voice (already applied or not needed)
250
+ const voice = clone.voice;
251
+ delete voice.channelOverrides;
252
+ return stringifyYaml(clone, { lineWidth: 0 });
253
+ }
254
+ /**
255
+ * Expanded natural language prompt with behavioral directives.
256
+ * Delegates to the existing prompt generator.
257
+ */
258
+ toPrompt(channel) {
259
+ return generateSystemPrompt(this._data, channel);
260
+ }
261
+ /** Full YAML including all metadata (for saving to file). */
262
+ toYAML() {
263
+ return stringifyYaml(structuredClone(this._data), { lineWidth: 0 });
264
+ }
265
+ /** Full JSON including all metadata. */
266
+ toJSON() {
267
+ return JSON.stringify(this._data, null, 2);
268
+ }
269
+ /** Validate and return errors. */
270
+ validate() {
271
+ return validateSpec(this._data);
272
+ }
273
+ /** Diff this Grain against another. Returns paths with before/after values. */
274
+ diff(other) {
275
+ const result = {};
276
+ diffObjects(this._data, other._data, "", result);
277
+ return result;
278
+ }
279
+ }
280
+ // =============================================================================
281
+ // HELPERS
282
+ // =============================================================================
283
+ function deepMerge(target, source) {
284
+ for (const key of Object.keys(source)) {
285
+ const sv = source[key];
286
+ const tv = target[key];
287
+ if (isPlainObject(sv) && isPlainObject(tv)) {
288
+ deepMerge(tv, sv);
289
+ }
290
+ else {
291
+ target[key] = structuredClone(sv);
292
+ }
293
+ }
294
+ }
295
+ function isPlainObject(v) {
296
+ return typeof v === "object" && v !== null && !Array.isArray(v);
297
+ }
298
+ function diffObjects(a, b, prefix, result) {
299
+ const allKeys = new Set([...Object.keys(a), ...Object.keys(b)]);
300
+ for (const key of allKeys) {
301
+ const path = prefix ? `${prefix}.${key}` : key;
302
+ const av = a[key];
303
+ const bv = b[key];
304
+ if (isPlainObject(av) && isPlainObject(bv)) {
305
+ diffObjects(av, bv, path, result);
306
+ }
307
+ else if (Array.isArray(av) && Array.isArray(bv)) {
308
+ if (JSON.stringify(av) !== JSON.stringify(bv)) {
309
+ result[path] = { before: av, after: bv };
310
+ }
311
+ }
312
+ else if (av !== bv) {
313
+ result[path] = { before: av, after: bv };
314
+ }
315
+ }
316
+ }
317
+ //# sourceMappingURL=grain.js.map