@aigne/ash 0.0.1

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 (146) hide show
  1. package/DESIGN.md +41 -0
  2. package/dist/ai-dev-loop/ash-run-result.cjs +12 -0
  3. package/dist/ai-dev-loop/ash-run-result.d.cts +28 -0
  4. package/dist/ai-dev-loop/ash-run-result.d.cts.map +1 -0
  5. package/dist/ai-dev-loop/ash-run-result.d.mts +28 -0
  6. package/dist/ai-dev-loop/ash-run-result.d.mts.map +1 -0
  7. package/dist/ai-dev-loop/ash-run-result.mjs +11 -0
  8. package/dist/ai-dev-loop/ash-run-result.mjs.map +1 -0
  9. package/dist/ai-dev-loop/ash-typed-error.cjs +51 -0
  10. package/dist/ai-dev-loop/ash-typed-error.d.cts +54 -0
  11. package/dist/ai-dev-loop/ash-typed-error.d.cts.map +1 -0
  12. package/dist/ai-dev-loop/ash-typed-error.d.mts +54 -0
  13. package/dist/ai-dev-loop/ash-typed-error.d.mts.map +1 -0
  14. package/dist/ai-dev-loop/ash-typed-error.mjs +50 -0
  15. package/dist/ai-dev-loop/ash-typed-error.mjs.map +1 -0
  16. package/dist/ai-dev-loop/ash-validate.cjs +27 -0
  17. package/dist/ai-dev-loop/ash-validate.d.cts +7 -0
  18. package/dist/ai-dev-loop/ash-validate.d.cts.map +1 -0
  19. package/dist/ai-dev-loop/ash-validate.d.mts +7 -0
  20. package/dist/ai-dev-loop/ash-validate.d.mts.map +1 -0
  21. package/dist/ai-dev-loop/ash-validate.mjs +28 -0
  22. package/dist/ai-dev-loop/ash-validate.mjs.map +1 -0
  23. package/dist/ai-dev-loop/dev-loop.cjs +134 -0
  24. package/dist/ai-dev-loop/dev-loop.d.cts +28 -0
  25. package/dist/ai-dev-loop/dev-loop.d.cts.map +1 -0
  26. package/dist/ai-dev-loop/dev-loop.d.mts +28 -0
  27. package/dist/ai-dev-loop/dev-loop.d.mts.map +1 -0
  28. package/dist/ai-dev-loop/dev-loop.mjs +135 -0
  29. package/dist/ai-dev-loop/dev-loop.mjs.map +1 -0
  30. package/dist/ai-dev-loop/index.cjs +24 -0
  31. package/dist/ai-dev-loop/index.d.cts +9 -0
  32. package/dist/ai-dev-loop/index.d.mts +9 -0
  33. package/dist/ai-dev-loop/index.mjs +10 -0
  34. package/dist/ai-dev-loop/live-mode.cjs +17 -0
  35. package/dist/ai-dev-loop/live-mode.d.cts +24 -0
  36. package/dist/ai-dev-loop/live-mode.d.cts.map +1 -0
  37. package/dist/ai-dev-loop/live-mode.d.mts +24 -0
  38. package/dist/ai-dev-loop/live-mode.d.mts.map +1 -0
  39. package/dist/ai-dev-loop/live-mode.mjs +17 -0
  40. package/dist/ai-dev-loop/live-mode.mjs.map +1 -0
  41. package/dist/ai-dev-loop/meta-tools.cjs +123 -0
  42. package/dist/ai-dev-loop/meta-tools.d.cts +24 -0
  43. package/dist/ai-dev-loop/meta-tools.d.cts.map +1 -0
  44. package/dist/ai-dev-loop/meta-tools.d.mts +24 -0
  45. package/dist/ai-dev-loop/meta-tools.d.mts.map +1 -0
  46. package/dist/ai-dev-loop/meta-tools.mjs +120 -0
  47. package/dist/ai-dev-loop/meta-tools.mjs.map +1 -0
  48. package/dist/ai-dev-loop/structured-runner.cjs +154 -0
  49. package/dist/ai-dev-loop/structured-runner.d.cts +12 -0
  50. package/dist/ai-dev-loop/structured-runner.d.cts.map +1 -0
  51. package/dist/ai-dev-loop/structured-runner.d.mts +12 -0
  52. package/dist/ai-dev-loop/structured-runner.d.mts.map +1 -0
  53. package/dist/ai-dev-loop/structured-runner.mjs +155 -0
  54. package/dist/ai-dev-loop/structured-runner.mjs.map +1 -0
  55. package/dist/ai-dev-loop/system-prompt.cjs +55 -0
  56. package/dist/ai-dev-loop/system-prompt.d.cts +20 -0
  57. package/dist/ai-dev-loop/system-prompt.d.cts.map +1 -0
  58. package/dist/ai-dev-loop/system-prompt.d.mts +20 -0
  59. package/dist/ai-dev-loop/system-prompt.d.mts.map +1 -0
  60. package/dist/ai-dev-loop/system-prompt.mjs +54 -0
  61. package/dist/ai-dev-loop/system-prompt.mjs.map +1 -0
  62. package/dist/ast.d.cts +140 -0
  63. package/dist/ast.d.cts.map +1 -0
  64. package/dist/ast.d.mts +140 -0
  65. package/dist/ast.d.mts.map +1 -0
  66. package/dist/compiler.cjs +802 -0
  67. package/dist/compiler.d.cts +103 -0
  68. package/dist/compiler.d.cts.map +1 -0
  69. package/dist/compiler.d.mts +103 -0
  70. package/dist/compiler.d.mts.map +1 -0
  71. package/dist/compiler.mjs +802 -0
  72. package/dist/compiler.mjs.map +1 -0
  73. package/dist/index.cjs +14 -0
  74. package/dist/index.d.cts +7 -0
  75. package/dist/index.d.mts +7 -0
  76. package/dist/index.mjs +7 -0
  77. package/dist/lexer.cjs +451 -0
  78. package/dist/lexer.d.cts +14 -0
  79. package/dist/lexer.d.cts.map +1 -0
  80. package/dist/lexer.d.mts +14 -0
  81. package/dist/lexer.d.mts.map +1 -0
  82. package/dist/lexer.mjs +451 -0
  83. package/dist/lexer.mjs.map +1 -0
  84. package/dist/parser.cjs +734 -0
  85. package/dist/parser.d.cts +40 -0
  86. package/dist/parser.d.cts.map +1 -0
  87. package/dist/parser.d.mts +40 -0
  88. package/dist/parser.d.mts.map +1 -0
  89. package/dist/parser.mjs +734 -0
  90. package/dist/parser.mjs.map +1 -0
  91. package/dist/reference.cjs +130 -0
  92. package/dist/reference.d.cts +11 -0
  93. package/dist/reference.d.cts.map +1 -0
  94. package/dist/reference.d.mts +11 -0
  95. package/dist/reference.d.mts.map +1 -0
  96. package/dist/reference.mjs +130 -0
  97. package/dist/reference.mjs.map +1 -0
  98. package/dist/template.cjs +85 -0
  99. package/dist/template.mjs +84 -0
  100. package/dist/template.mjs.map +1 -0
  101. package/dist/type-checker.cjs +582 -0
  102. package/dist/type-checker.d.cts +31 -0
  103. package/dist/type-checker.d.cts.map +1 -0
  104. package/dist/type-checker.d.mts +31 -0
  105. package/dist/type-checker.d.mts.map +1 -0
  106. package/dist/type-checker.mjs +573 -0
  107. package/dist/type-checker.mjs.map +1 -0
  108. package/package.json +29 -0
  109. package/src/ai-dev-loop/ash-run-result.test.ts +113 -0
  110. package/src/ai-dev-loop/ash-run-result.ts +46 -0
  111. package/src/ai-dev-loop/ash-typed-error.test.ts +136 -0
  112. package/src/ai-dev-loop/ash-typed-error.ts +50 -0
  113. package/src/ai-dev-loop/ash-validate.test.ts +54 -0
  114. package/src/ai-dev-loop/ash-validate.ts +34 -0
  115. package/src/ai-dev-loop/dev-loop.test.ts +364 -0
  116. package/src/ai-dev-loop/dev-loop.ts +156 -0
  117. package/src/ai-dev-loop/dry-run.test.ts +107 -0
  118. package/src/ai-dev-loop/e2e-multi-fix.test.ts +473 -0
  119. package/src/ai-dev-loop/e2e.test.ts +324 -0
  120. package/src/ai-dev-loop/index.ts +15 -0
  121. package/src/ai-dev-loop/invariants.test.ts +253 -0
  122. package/src/ai-dev-loop/live-mode.test.ts +63 -0
  123. package/src/ai-dev-loop/live-mode.ts +33 -0
  124. package/src/ai-dev-loop/meta-tools.test.ts +120 -0
  125. package/src/ai-dev-loop/meta-tools.ts +142 -0
  126. package/src/ai-dev-loop/structured-runner.test.ts +159 -0
  127. package/src/ai-dev-loop/structured-runner.ts +209 -0
  128. package/src/ai-dev-loop/system-prompt.test.ts +102 -0
  129. package/src/ai-dev-loop/system-prompt.ts +81 -0
  130. package/src/ast.ts +186 -0
  131. package/src/compiler.test.ts +2933 -0
  132. package/src/compiler.ts +1103 -0
  133. package/src/e2e.test.ts +552 -0
  134. package/src/index.ts +16 -0
  135. package/src/lexer.test.ts +538 -0
  136. package/src/lexer.ts +222 -0
  137. package/src/parser.test.ts +1024 -0
  138. package/src/parser.ts +835 -0
  139. package/src/reference.test.ts +166 -0
  140. package/src/reference.ts +125 -0
  141. package/src/template.test.ts +210 -0
  142. package/src/template.ts +139 -0
  143. package/src/type-checker.test.ts +1494 -0
  144. package/src/type-checker.ts +785 -0
  145. package/tsconfig.json +9 -0
  146. package/tsdown.config.ts +12 -0
@@ -0,0 +1,102 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { buildSystemPrompt, buildCorrectionPrompt } from "./system-prompt.js";
3
+ import type { AshRunFailure } from "./ash-run-result.js";
4
+
5
+ describe("System Prompt Assembly", () => {
6
+ // ── buildSystemPrompt ──────────────────────────────────
7
+
8
+ it("contains rules, tools, and ASH reference", () => {
9
+ const prompt = buildSystemPrompt({ max_iterations: 3 });
10
+ expect(prompt).toContain("You are an AOS agent");
11
+ expect(prompt).toContain("ash.validate");
12
+ expect(prompt).toContain("ash.run");
13
+ expect(prompt).toContain("ash.explain_error");
14
+ expect(prompt).toContain("ASH quick reference");
15
+ });
16
+
17
+ it("shows max_iterations count", () => {
18
+ const prompt = buildSystemPrompt({ max_iterations: 3 });
19
+ expect(prompt).toContain("3 attempts");
20
+ });
21
+
22
+ it("singular attempt for max_iterations=1", () => {
23
+ const prompt = buildSystemPrompt({ max_iterations: 1 });
24
+ expect(prompt).toContain("1 attempt");
25
+ expect(prompt).not.toContain("1 attempts");
26
+ });
27
+
28
+ it("rejects max_iterations=0", () => {
29
+ expect(() => buildSystemPrompt({ max_iterations: 0 })).toThrow();
30
+ });
31
+
32
+ it("does not contain API keys or internal paths", () => {
33
+ const prompt = buildSystemPrompt({ max_iterations: 5 });
34
+ expect(prompt).not.toMatch(/api[_-]?key/i);
35
+ expect(prompt).not.toMatch(/\/Users\//);
36
+ expect(prompt).not.toMatch(/\/home\//);
37
+ });
38
+
39
+ // ── buildCorrectionPrompt ──────────────────────────────
40
+
41
+ it("contains step, error kind, detail, and suggestion", () => {
42
+ const failure: AshRunFailure = {
43
+ status: "error",
44
+ steps: [{ step: 1, command: "find", status: "ok", duration_ms: 10, output: [] }],
45
+ failedAt: { kind: "ToolNotFound", name: "foo", available: ["find", "where"], message: "not found" },
46
+ duration_ms: 20,
47
+ };
48
+ const prompt = buildCorrectionPrompt(failure, 'job "test" { find /users }');
49
+ expect(prompt).toContain("ToolNotFound");
50
+ expect(prompt).toContain("not found");
51
+ expect(prompt).toContain('job "test" { find /users }');
52
+ });
53
+
54
+ it("includes previous script", () => {
55
+ const failure: AshRunFailure = {
56
+ status: "error",
57
+ steps: [],
58
+ failedAt: { kind: "ParseError", message: "unexpected token", line: 3 },
59
+ duration_ms: 5,
60
+ };
61
+ const script = 'job "broken" {\n find /x\n ???\n}';
62
+ const prompt = buildCorrectionPrompt(failure, script);
63
+ expect(prompt).toContain(script);
64
+ });
65
+
66
+ it("omits suggestion line when error has no suggestion", () => {
67
+ const failure: AshRunFailure = {
68
+ status: "error",
69
+ steps: [],
70
+ failedAt: { kind: "RuntimeError", message: "boom" },
71
+ duration_ms: 1,
72
+ };
73
+ const prompt = buildCorrectionPrompt(failure, "output hello");
74
+ expect(prompt).toContain("RuntimeError");
75
+ expect(prompt).not.toMatch(/Suggestion:.*undefined/);
76
+ });
77
+
78
+ it("does not truncate very long script", () => {
79
+ const failure: AshRunFailure = {
80
+ status: "error",
81
+ steps: [],
82
+ failedAt: { kind: "Timeout", step: "find", limit_ms: 1000, message: "timeout" },
83
+ duration_ms: 1000,
84
+ };
85
+ const longScript = "find /users\n".repeat(1000);
86
+ const prompt = buildCorrectionPrompt(failure, longScript);
87
+ expect(prompt).toContain(longScript);
88
+ });
89
+
90
+ it("does not leak world data beyond the failing step", () => {
91
+ const failure: AshRunFailure = {
92
+ status: "error",
93
+ steps: [
94
+ { step: 1, command: "find", status: "ok", duration_ms: 10, output: [{ secret: "password123" }] },
95
+ ],
96
+ failedAt: { kind: "RuntimeError", message: "step 2 failed" },
97
+ duration_ms: 20,
98
+ };
99
+ const prompt = buildCorrectionPrompt(failure, "find /users | save /out");
100
+ expect(prompt).not.toContain("password123");
101
+ });
102
+ });
@@ -0,0 +1,81 @@
1
+ /**
2
+ * System Prompt Assembly for ASH AI Dev Loop.
3
+ *
4
+ * Implements the prompt contracts from INTENT §5.1 and §5.2.
5
+ */
6
+
7
+ import type { AshRunFailure } from "./ash-run-result.js";
8
+ import type { AshTypedError } from "./ash-typed-error.js";
9
+
10
+ export interface LoopConfig {
11
+ max_iterations: number;
12
+ }
13
+
14
+ /**
15
+ * Build the initial system prompt for the AI dev loop (INTENT §5.1).
16
+ */
17
+ export function buildSystemPrompt(config: LoopConfig): string {
18
+ if (config.max_iterations < 1) {
19
+ throw new Error("max_iterations must be at least 1");
20
+ }
21
+ const plural = config.max_iterations === 1 ? "attempt" : "attempts";
22
+ return `You are an AOS agent. You operate inside AOS by writing and running ASH scripts.
23
+
24
+ Rules:
25
+ 1. Write ASH scripts to accomplish the user's intent.
26
+ 2. Use ash.validate before ash.run.
27
+ 3. If ash.run fails, read the structured error carefully.
28
+ 4. Fix only the failing step. Do not rewrite unrelated steps.
29
+ 5. You have at most ${config.max_iterations} ${plural}.
30
+
31
+ Available tools:
32
+ - ash.validate(script) → ParseError[]
33
+ - ash.run(script, mode="dry-run") → AshRunResult
34
+ - ash.explain_error(error) → explanation + suggestions
35
+
36
+ ASH quick reference:
37
+ - discover intent="..." → find tools
38
+ - pick [n] → select from results
39
+ - exec <tool> [args] → run tool (goes through Intent Gate)
40
+ - read/write/edit → AFS operations
41
+ - pipe with | → chain steps`;
42
+ }
43
+
44
+ /**
45
+ * Build the correction prompt for a failed iteration (INTENT §5.2).
46
+ *
47
+ * Only includes the error info and previous script — never leaks
48
+ * step output data to avoid world data exposure.
49
+ */
50
+ export function buildCorrectionPrompt(failure: AshRunFailure, script: string): string {
51
+ const err = failure.failedAt as AshTypedError;
52
+ const failedStep = failure.steps.length > 0
53
+ ? failure.steps[failure.steps.length - 1]
54
+ : undefined;
55
+
56
+ const lines = [
57
+ "The previous ASH script failed.",
58
+ "",
59
+ "Failure:",
60
+ `- Step: ${failedStep ? `${failedStep.step} (${failedStep.command})` : "unknown"}`,
61
+ `- Error: ${err.kind}`,
62
+ `- Detail: ${err.message}`,
63
+ ];
64
+
65
+ const suggestion = (err as any).suggestion;
66
+ if (suggestion) {
67
+ lines.push(`- Suggestion: ${suggestion}`);
68
+ }
69
+
70
+ lines.push(
71
+ "",
72
+ "Previous script:",
73
+ "---",
74
+ script,
75
+ "---",
76
+ "",
77
+ "Fix the script. Only change the failing step unless the fix requires structural changes.",
78
+ );
79
+
80
+ return lines.join("\n");
81
+ }
package/src/ast.ts ADDED
@@ -0,0 +1,186 @@
1
+ export interface Annotation {
2
+ name: string;
3
+ args: string[];
4
+ line: number;
5
+ column: number;
6
+ }
7
+
8
+ export interface QueryCondition {
9
+ field: string;
10
+ op: "==" | "!=" | ">" | "<" | ">=" | "<=";
11
+ value: string | number;
12
+ }
13
+
14
+ export interface FindExpression {
15
+ kind: "find";
16
+ path: string;
17
+ query?: QueryCondition;
18
+ }
19
+
20
+ export interface WhereClause {
21
+ kind: "where";
22
+ left: string;
23
+ op: "==" | "!=" | ">" | "<" | ">=" | "<=";
24
+ right: string | number;
25
+ }
26
+
27
+ // ── Expression types (v3.1: restricted expression system) ──
28
+
29
+ export interface BinaryExpression {
30
+ kind: "binary";
31
+ op: "+" | "-" | "*" | "/";
32
+ left: Expression;
33
+ right: Expression;
34
+ }
35
+
36
+ export interface FieldAccessExpression {
37
+ kind: "field_access";
38
+ path: string; // dot-separated, e.g. "user.name"
39
+ }
40
+
41
+ export interface LiteralExpression {
42
+ kind: "literal";
43
+ value: string | number;
44
+ }
45
+
46
+ export interface VarRefExpression {
47
+ kind: "var_ref";
48
+ name: string; // without $
49
+ }
50
+
51
+ export type Expression =
52
+ | BinaryExpression
53
+ | FieldAccessExpression
54
+ | LiteralExpression
55
+ | VarRefExpression;
56
+
57
+ export interface MapExpression {
58
+ kind: "map";
59
+ field: string; // dot-separated, e.g. "user.name" (single-field mode)
60
+ mappings?: Record<string, string>; // { outputKey: inputField } (object mapping mode, no colon)
61
+ expression?: Expression; // single-expression mode: map name + " " + surname
62
+ exprMappings?: Record<string, Expression>; // expression object mode: map { key: expr, ... }
63
+ }
64
+
65
+ export interface SaveExpression {
66
+ kind: "save";
67
+ path: string;
68
+ }
69
+
70
+ export interface PublishExpression {
71
+ kind: "publish";
72
+ path: string;
73
+ }
74
+
75
+ export interface TeeExpression {
76
+ kind: "tee";
77
+ path: string;
78
+ }
79
+
80
+ export interface FanoutExpression {
81
+ kind: "fanout";
82
+ branches: PipelineStage[][];
83
+ }
84
+
85
+ export interface OutputExpression {
86
+ kind: "output";
87
+ message: string;
88
+ expression?: Expression;
89
+ }
90
+
91
+ export interface InputExpression {
92
+ kind: "input";
93
+ prompt: string;
94
+ }
95
+
96
+ export interface CountExpression {
97
+ kind: "count";
98
+ }
99
+
100
+ export interface GroupByExpression {
101
+ kind: "group-by";
102
+ field: string;
103
+ }
104
+
105
+ export interface ActionExpression {
106
+ kind: "action";
107
+ path: string;
108
+ relative?: boolean;
109
+ params?: Record<string, unknown>;
110
+ }
111
+
112
+ export interface RouteBranch {
113
+ value: string; // match value, or "_" for fallback
114
+ targetJob: string;
115
+ }
116
+
117
+ export interface RouteExpression {
118
+ kind: "route";
119
+ field: string;
120
+ branches: RouteBranch[];
121
+ fallback?: string; // fallback job name (from `_ -> job name`)
122
+ }
123
+
124
+ export interface LookupExpression {
125
+ kind: "lookup";
126
+ path: string;
127
+ joinKey: string; // field to join on
128
+ }
129
+
130
+ export type PipelineStage =
131
+ | FindExpression
132
+ | WhereClause
133
+ | MapExpression
134
+ | SaveExpression
135
+ | PublishExpression
136
+ | TeeExpression
137
+ | FanoutExpression
138
+ | OutputExpression
139
+ | InputExpression
140
+ | CountExpression
141
+ | GroupByExpression
142
+ | ActionExpression
143
+ | RouteExpression
144
+ | LookupExpression;
145
+
146
+ export interface EventTrigger {
147
+ kind: "event";
148
+ path: string;
149
+ event: string;
150
+ }
151
+
152
+ export interface CronTrigger {
153
+ kind: "cron";
154
+ expression: string;
155
+ }
156
+
157
+ export type TriggerDeclaration = EventTrigger | CronTrigger;
158
+
159
+ export interface JobDeclaration {
160
+ kind: "job";
161
+ name: string;
162
+ annotations: Annotation[];
163
+ pipeline: PipelineStage[];
164
+ trigger?: TriggerDeclaration;
165
+ }
166
+
167
+ export interface LetStatement {
168
+ kind: "let";
169
+ name: string;
170
+ value: string | number;
171
+ pipeline?: PipelineStage[]; // runtime let: let x = find /data | count
172
+ }
173
+
174
+ export interface ParamDeclaration {
175
+ kind: "param";
176
+ name: string;
177
+ defaultValue: string | number;
178
+ }
179
+
180
+ export type TopLevelStatement = JobDeclaration | OutputExpression | LetStatement | ParamDeclaration;
181
+
182
+ export interface Program {
183
+ statements: TopLevelStatement[];
184
+ /** Backward-compatible accessor: returns only job declarations */
185
+ jobs: JobDeclaration[];
186
+ }