@prismatic-io/lux 0.0.1 → 0.0.2-preview.4

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 (142) hide show
  1. package/lib/answerers/terminal/index.js +1 -1
  2. package/lib/answerers/terminal/index.js.map +1 -1
  3. package/lib/assertions/authoring.d.ts +14 -0
  4. package/lib/assertions/authoring.d.ts.map +1 -0
  5. package/lib/assertions/authoring.js +3 -0
  6. package/lib/assertions/authoring.js.map +1 -0
  7. package/lib/assertions/core/helpers.d.ts +2 -7
  8. package/lib/assertions/core/helpers.d.ts.map +1 -1
  9. package/lib/assertions/core/helpers.js +2 -6
  10. package/lib/assertions/core/helpers.js.map +1 -1
  11. package/lib/authoring.d.ts +1 -0
  12. package/lib/authoring.d.ts.map +1 -1
  13. package/lib/authoring.js +1 -0
  14. package/lib/authoring.js.map +1 -1
  15. package/lib/cli/program.d.ts.map +1 -1
  16. package/lib/cli/program.js +6 -2
  17. package/lib/cli/program.js.map +1 -1
  18. package/lib/cli/render/reporter.d.ts.map +1 -1
  19. package/lib/cli/render/reporter.js +16 -3
  20. package/lib/cli/render/reporter.js.map +1 -1
  21. package/lib/cli/run-options.d.ts +1 -0
  22. package/lib/cli/run-options.d.ts.map +1 -1
  23. package/lib/cli/run-options.js.map +1 -1
  24. package/lib/core/artifact-evidence.d.ts +8 -0
  25. package/lib/core/artifact-evidence.d.ts.map +1 -1
  26. package/lib/core/artifact-evidence.js +36 -4
  27. package/lib/core/artifact-evidence.js.map +1 -1
  28. package/lib/core/assertion.d.ts +19 -9
  29. package/lib/core/assertion.d.ts.map +1 -1
  30. package/lib/core/assertion.js +26 -0
  31. package/lib/core/assertion.js.map +1 -1
  32. package/lib/core/case-authoring-state.d.ts +12 -0
  33. package/lib/core/case-authoring-state.d.ts.map +1 -0
  34. package/lib/core/case-authoring-state.js +43 -0
  35. package/lib/core/case-authoring-state.js.map +1 -0
  36. package/lib/core/case.d.ts +29 -1
  37. package/lib/core/case.d.ts.map +1 -1
  38. package/lib/core/case.js +14 -6
  39. package/lib/core/case.js.map +1 -1
  40. package/lib/core/driver.d.ts +3 -0
  41. package/lib/core/driver.d.ts.map +1 -1
  42. package/lib/core/driver.js.map +1 -1
  43. package/lib/core/events.d.ts +2 -0
  44. package/lib/core/events.d.ts.map +1 -1
  45. package/lib/core/events.js +1 -0
  46. package/lib/core/events.js.map +1 -1
  47. package/lib/core/lifecycle-fixtures.d.ts +3 -1
  48. package/lib/core/lifecycle-fixtures.d.ts.map +1 -1
  49. package/lib/core/lifecycle-fixtures.js +4 -2
  50. package/lib/core/lifecycle-fixtures.js.map +1 -1
  51. package/lib/core/lifecycle-hooks.d.ts +9 -0
  52. package/lib/core/lifecycle-hooks.d.ts.map +1 -1
  53. package/lib/core/lifecycle-hooks.js +8 -2
  54. package/lib/core/lifecycle-hooks.js.map +1 -1
  55. package/lib/core/run.d.ts +1 -0
  56. package/lib/core/run.d.ts.map +1 -1
  57. package/lib/core/wire.d.ts +1 -0
  58. package/lib/core/wire.d.ts.map +1 -1
  59. package/lib/core/wire.js +5 -1
  60. package/lib/core/wire.js.map +1 -1
  61. package/lib/drivers/subprocess/index.d.ts.map +1 -1
  62. package/lib/drivers/subprocess/index.js +6 -0
  63. package/lib/drivers/subprocess/index.js.map +1 -1
  64. package/lib/index.d.ts +4 -4
  65. package/lib/index.d.ts.map +1 -1
  66. package/lib/index.js +4 -4
  67. package/lib/index.js.map +1 -1
  68. package/lib/orchestrator/annotation-store.d.ts.map +1 -1
  69. package/lib/orchestrator/annotation-store.js +6 -2
  70. package/lib/orchestrator/annotation-store.js.map +1 -1
  71. package/lib/orchestrator/case-extensions.d.ts +6 -0
  72. package/lib/orchestrator/case-extensions.d.ts.map +1 -0
  73. package/lib/orchestrator/case-extensions.js +28 -0
  74. package/lib/orchestrator/case-extensions.js.map +1 -0
  75. package/lib/orchestrator/config.d.ts.map +1 -1
  76. package/lib/orchestrator/config.js +0 -2
  77. package/lib/orchestrator/config.js.map +1 -1
  78. package/lib/orchestrator/doctor.d.ts.map +1 -1
  79. package/lib/orchestrator/doctor.js +5 -3
  80. package/lib/orchestrator/doctor.js.map +1 -1
  81. package/lib/orchestrator/grader-experiment.d.ts +12 -0
  82. package/lib/orchestrator/grader-experiment.d.ts.map +1 -1
  83. package/lib/orchestrator/grading.d.ts +2 -1
  84. package/lib/orchestrator/grading.d.ts.map +1 -1
  85. package/lib/orchestrator/grading.js +16 -4
  86. package/lib/orchestrator/grading.js.map +1 -1
  87. package/lib/orchestrator/loader.d.ts.map +1 -1
  88. package/lib/orchestrator/loader.js +2 -0
  89. package/lib/orchestrator/loader.js.map +1 -1
  90. package/lib/orchestrator/orchestrator.d.ts +3 -0
  91. package/lib/orchestrator/orchestrator.d.ts.map +1 -1
  92. package/lib/orchestrator/orchestrator.js +42 -14
  93. package/lib/orchestrator/orchestrator.js.map +1 -1
  94. package/lib/orchestrator/owned-lock.d.ts.map +1 -1
  95. package/lib/orchestrator/owned-lock.js +52 -38
  96. package/lib/orchestrator/owned-lock.js.map +1 -1
  97. package/lib/orchestrator/regrade.d.ts +6 -0
  98. package/lib/orchestrator/regrade.d.ts.map +1 -1
  99. package/lib/orchestrator/regrade.js +8 -4
  100. package/lib/orchestrator/regrade.js.map +1 -1
  101. package/lib/orchestrator/run-execution.d.ts +2 -1
  102. package/lib/orchestrator/run-execution.d.ts.map +1 -1
  103. package/lib/orchestrator/run-execution.js +3 -2
  104. package/lib/orchestrator/run-execution.js.map +1 -1
  105. package/package.json +1 -2
  106. package/skills/lux/SKILL.md +7 -3
  107. package/skills/lux/references/cli.md +25 -2
  108. package/skills/lux/references/eval-authoring.md +156 -4
  109. package/skills/lux-answerer/SKILL.md +1 -1
  110. package/src/answerers/terminal/index.ts +1 -1
  111. package/src/assertions/authoring.ts +79 -0
  112. package/src/assertions/core/helpers.ts +3 -6
  113. package/src/authoring.ts +1 -0
  114. package/src/cli/program.ts +9 -2
  115. package/src/cli/render/reporter.ts +18 -3
  116. package/src/cli/run-options.ts +1 -0
  117. package/src/core/artifact-evidence.ts +47 -4
  118. package/src/core/assertion.ts +41 -22
  119. package/src/core/case-authoring-state.ts +65 -0
  120. package/src/core/case.ts +57 -6
  121. package/src/core/driver.ts +3 -0
  122. package/src/core/events.ts +1 -0
  123. package/src/core/lifecycle-fixtures.ts +7 -2
  124. package/src/core/lifecycle-hooks.ts +16 -2
  125. package/src/core/wire.ts +5 -1
  126. package/src/drivers/subprocess/index.ts +6 -0
  127. package/src/index.ts +10 -6
  128. package/src/orchestrator/annotation-store.ts +5 -1
  129. package/src/orchestrator/case-extensions.ts +44 -0
  130. package/src/orchestrator/config.ts +0 -2
  131. package/src/orchestrator/doctor.ts +5 -3
  132. package/src/orchestrator/grading.ts +21 -2
  133. package/src/orchestrator/loader.ts +2 -0
  134. package/src/orchestrator/orchestrator.ts +86 -40
  135. package/src/orchestrator/owned-lock.ts +54 -34
  136. package/src/orchestrator/regrade.ts +8 -7
  137. package/src/orchestrator/run-execution.ts +5 -1
  138. package/lib/answerers/scripted/index.d.ts +0 -8
  139. package/lib/answerers/scripted/index.d.ts.map +0 -1
  140. package/lib/answerers/scripted/index.js +0 -163
  141. package/lib/answerers/scripted/index.js.map +0 -1
  142. package/src/answerers/scripted/index.ts +0 -186
@@ -1,163 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { resolve } from "node:path";
3
- import { load as loadYaml } from "js-yaml";
4
- import { z } from "zod";
5
- import { formatZodIssues, InterruptKindSchema, } from "../../core/index.js";
6
- import { approveAnswer } from "../shared.js";
7
- const MatchSchema = z
8
- .object({
9
- kind: InterruptKindSchema.optional(),
10
- id: z.string().optional(),
11
- question_contains: z.string().optional(),
12
- question_matches: z.string().optional(),
13
- request_contains: z.string().optional(),
14
- })
15
- .strict();
16
- const AnsweredAskSchema = z.object({ answer: z.string() }).strict();
17
- const AnsweredApproveSchema = z
18
- .object({ approved: z.boolean(), reason: z.string().optional() })
19
- .strict();
20
- const AnsweredCustomSchema = z.object({ payload: z.unknown() }).strict();
21
- const RuleSchema = z.union([
22
- z.object({ match: MatchSchema, answer: z.string() }).strict(),
23
- z.object({ match: MatchSchema, approved: z.boolean(), reason: z.string().optional() }).strict(),
24
- z.object({ match: MatchSchema, payload: z.unknown() }).strict(),
25
- z.object({ default: AnsweredAskSchema }).strict(),
26
- z.object({ default: AnsweredApproveSchema }).strict(),
27
- z.object({ default: AnsweredCustomSchema }).strict(),
28
- ]);
29
- const FixtureSchema = z.array(RuleSchema);
30
- export const ScriptedAnswererConfigSchema = z.object({ fixturePath: z.string() }).strict();
31
- const ruleLabel = (path, index) => `rule ${index + 1} of ${path}`;
32
- const bodyKind = (rule) => {
33
- const body = "default" in rule ? rule.default : rule;
34
- if ("answer" in body)
35
- return "ask";
36
- if ("approved" in body)
37
- return "approve";
38
- return "custom";
39
- };
40
- const matchableKind = (m) => {
41
- if (m.kind)
42
- return m.kind;
43
- if (m.question_contains !== undefined || m.question_matches !== undefined)
44
- return "ask";
45
- if (m.request_contains !== undefined)
46
- return "approve";
47
- return undefined;
48
- };
49
- const compileQuestionPattern = (rule, path, index) => {
50
- if (!("match" in rule) || rule.match.question_matches === undefined)
51
- return undefined;
52
- try {
53
- return new RegExp(rule.match.question_matches);
54
- }
55
- catch (err) {
56
- throw new Error(`scripted: invalid question_matches in ${ruleLabel(path, index)}: ${err instanceof Error ? err.message : String(err)}`);
57
- }
58
- };
59
- // The rule's match fields and answer body are independently valid shapes, so
60
- // their interrupt kinds still need a semantic cross-check.
61
- const prepareRule = (rule, path, index) => {
62
- if ("match" in rule) {
63
- const wanted = matchableKind(rule.match);
64
- const answered = bodyKind(rule);
65
- if (wanted !== undefined && wanted !== answered) {
66
- throw new Error(`scripted: ${ruleLabel(path, index)} matches ${wanted} interrupts but its body answers ${answered}`);
67
- }
68
- }
69
- return { rule, questionPattern: compileQuestionPattern(rule, path, index) };
70
- };
71
- const matches = ({ rule, questionPattern }, interrupt) => {
72
- if (!("match" in rule))
73
- return false;
74
- const m = rule.match;
75
- if (m.kind && m.kind !== interrupt.kind)
76
- return false;
77
- if (m.id && m.id !== interrupt.id)
78
- return false;
79
- if (m.question_contains) {
80
- if (interrupt.kind !== "ask")
81
- return false;
82
- if (!interrupt.question.includes(m.question_contains))
83
- return false;
84
- }
85
- if (questionPattern) {
86
- if (interrupt.kind !== "ask")
87
- return false;
88
- if (!questionPattern.test(interrupt.question))
89
- return false;
90
- }
91
- if (m.request_contains) {
92
- if (interrupt.kind !== "approve")
93
- return false;
94
- if (!interrupt.request.includes(m.request_contains))
95
- return false;
96
- }
97
- return true;
98
- };
99
- // A fixture may carry one default per interrupt kind; the shape of the
100
- // default's body is what says which kind it can answer.
101
- const defaultAnswersKind = (rule, kind) => {
102
- if (!("default" in rule))
103
- return false;
104
- const body = rule.default;
105
- if (kind === "ask")
106
- return "answer" in body;
107
- if (kind === "approve")
108
- return "approved" in body;
109
- return "payload" in body;
110
- };
111
- const buildAnswer = (rule, interrupt) => {
112
- const body = "default" in rule ? rule.default : rule;
113
- if (interrupt.kind === "ask") {
114
- if (!("answer" in body)) {
115
- throw new Error(`scripted: rule for ask interrupt missing 'answer' (id=${interrupt.id})`);
116
- }
117
- return { kind: "ask", id: interrupt.id, text: body.answer };
118
- }
119
- if (interrupt.kind === "approve") {
120
- if (!("approved" in body)) {
121
- throw new Error(`scripted: rule for approve interrupt missing 'approved' (id=${interrupt.id})`);
122
- }
123
- return approveAnswer(interrupt.id, body.approved, body.reason);
124
- }
125
- if (!("payload" in body)) {
126
- throw new Error(`scripted: rule for custom interrupt missing 'payload' (id=${interrupt.id})`);
127
- }
128
- return { kind: "custom", id: interrupt.id, payload: body.payload };
129
- };
130
- class ScriptedAnswererImpl {
131
- rules = [];
132
- config;
133
- constructor(config) {
134
- this.config = config;
135
- }
136
- async prepare() {
137
- const path = resolve(this.config.fixturePath);
138
- const raw = loadYaml(await readFile(path, "utf-8"));
139
- const parsed = FixtureSchema.safeParse(raw);
140
- if (!parsed.success) {
141
- throw new Error(`scripted: invalid fixture at ${path}: ${formatZodIssues(parsed.error)}`);
142
- }
143
- this.rules = parsed.data.map((rule, index) => prepareRule(rule, path, index));
144
- }
145
- async answer(interrupt) {
146
- const matched = this.rules.find((r) => matches(r, interrupt));
147
- if (matched) {
148
- return buildAnswer(matched.rule, interrupt);
149
- }
150
- const fallback = this.rules.find((r) => defaultAnswersKind(r.rule, interrupt.kind));
151
- if (fallback) {
152
- return buildAnswer(fallback.rule, interrupt);
153
- }
154
- throw new Error(`scripted: no rule matched interrupt (kind=${interrupt.kind}, id=${interrupt.id}) and no default provided`);
155
- }
156
- async close() { }
157
- }
158
- export const scriptedAnswerer = {
159
- name: "scripted",
160
- configSchema: ScriptedAnswererConfigSchema,
161
- create: (config) => new ScriptedAnswererImpl(config),
162
- };
163
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/answerers/scripted/index.ts"],"names":[],"mappings":"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"}
@@ -1,186 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { resolve } from "node:path";
3
- import { load as loadYaml } from "js-yaml";
4
- import { z } from "zod";
5
- import {
6
- type Answer,
7
- type Answerer,
8
- type AnswererPlugin,
9
- formatZodIssues,
10
- type Interrupt,
11
- InterruptKindSchema,
12
- } from "../../core/index.js";
13
- import { approveAnswer } from "../shared.js";
14
-
15
- const MatchSchema = z
16
- .object({
17
- kind: InterruptKindSchema.optional(),
18
- id: z.string().optional(),
19
- question_contains: z.string().optional(),
20
- question_matches: z.string().optional(),
21
- request_contains: z.string().optional(),
22
- })
23
- .strict();
24
-
25
- const AnsweredAskSchema = z.object({ answer: z.string() }).strict();
26
- const AnsweredApproveSchema = z
27
- .object({ approved: z.boolean(), reason: z.string().optional() })
28
- .strict();
29
- const AnsweredCustomSchema = z.object({ payload: z.unknown() }).strict();
30
-
31
- const RuleSchema = z.union([
32
- z.object({ match: MatchSchema, answer: z.string() }).strict(),
33
- z.object({ match: MatchSchema, approved: z.boolean(), reason: z.string().optional() }).strict(),
34
- z.object({ match: MatchSchema, payload: z.unknown() }).strict(),
35
- z.object({ default: AnsweredAskSchema }).strict(),
36
- z.object({ default: AnsweredApproveSchema }).strict(),
37
- z.object({ default: AnsweredCustomSchema }).strict(),
38
- ]);
39
-
40
- const FixtureSchema = z.array(RuleSchema);
41
-
42
- export const ScriptedAnswererConfigSchema = z.object({ fixturePath: z.string() }).strict();
43
-
44
- export type ScriptedAnswererConfig = z.infer<typeof ScriptedAnswererConfigSchema>;
45
-
46
- type Rule = z.infer<typeof RuleSchema>;
47
-
48
- /** A rule vetted at load time: kind/body cross-checked, regex pre-compiled. */
49
- type PreparedRule = { rule: Rule; questionPattern: RegExp | undefined };
50
-
51
- const ruleLabel = (path: string, index: number): string => `rule ${index + 1} of ${path}`;
52
-
53
- const bodyKind = (rule: Rule): Interrupt["kind"] => {
54
- const body = "default" in rule ? rule.default : rule;
55
- if ("answer" in body) return "ask";
56
- if ("approved" in body) return "approve";
57
- return "custom";
58
- };
59
-
60
- const matchableKind = (m: z.infer<typeof MatchSchema>): Interrupt["kind"] | undefined => {
61
- if (m.kind) return m.kind;
62
- if (m.question_contains !== undefined || m.question_matches !== undefined) return "ask";
63
- if (m.request_contains !== undefined) return "approve";
64
- return undefined;
65
- };
66
-
67
- const compileQuestionPattern = (rule: Rule, path: string, index: number): RegExp | undefined => {
68
- if (!("match" in rule) || rule.match.question_matches === undefined) return undefined;
69
- try {
70
- return new RegExp(rule.match.question_matches);
71
- } catch (err) {
72
- throw new Error(
73
- `scripted: invalid question_matches in ${ruleLabel(path, index)}: ${
74
- err instanceof Error ? err.message : String(err)
75
- }`,
76
- );
77
- }
78
- };
79
-
80
- // The rule's match fields and answer body are independently valid shapes, so
81
- // their interrupt kinds still need a semantic cross-check.
82
- const prepareRule = (rule: Rule, path: string, index: number): PreparedRule => {
83
- if ("match" in rule) {
84
- const wanted = matchableKind(rule.match);
85
- const answered = bodyKind(rule);
86
- if (wanted !== undefined && wanted !== answered) {
87
- throw new Error(
88
- `scripted: ${ruleLabel(path, index)} matches ${wanted} interrupts but its body answers ${answered}`,
89
- );
90
- }
91
- }
92
- return { rule, questionPattern: compileQuestionPattern(rule, path, index) };
93
- };
94
-
95
- const matches = ({ rule, questionPattern }: PreparedRule, interrupt: Interrupt): boolean => {
96
- if (!("match" in rule)) return false;
97
- const m = rule.match;
98
- if (m.kind && m.kind !== interrupt.kind) return false;
99
- if (m.id && m.id !== interrupt.id) return false;
100
- if (m.question_contains) {
101
- if (interrupt.kind !== "ask") return false;
102
- if (!interrupt.question.includes(m.question_contains)) return false;
103
- }
104
- if (questionPattern) {
105
- if (interrupt.kind !== "ask") return false;
106
- if (!questionPattern.test(interrupt.question)) return false;
107
- }
108
- if (m.request_contains) {
109
- if (interrupt.kind !== "approve") return false;
110
- if (!interrupt.request.includes(m.request_contains)) return false;
111
- }
112
- return true;
113
- };
114
-
115
- // A fixture may carry one default per interrupt kind; the shape of the
116
- // default's body is what says which kind it can answer.
117
- const defaultAnswersKind = (rule: Rule, kind: Interrupt["kind"]): boolean => {
118
- if (!("default" in rule)) return false;
119
- const body = rule.default;
120
- if (kind === "ask") return "answer" in body;
121
- if (kind === "approve") return "approved" in body;
122
- return "payload" in body;
123
- };
124
-
125
- const buildAnswer = (rule: Rule, interrupt: Interrupt): Answer => {
126
- const body = "default" in rule ? rule.default : rule;
127
- if (interrupt.kind === "ask") {
128
- if (!("answer" in body)) {
129
- throw new Error(`scripted: rule for ask interrupt missing 'answer' (id=${interrupt.id})`);
130
- }
131
- return { kind: "ask", id: interrupt.id, text: body.answer };
132
- }
133
- if (interrupt.kind === "approve") {
134
- if (!("approved" in body)) {
135
- throw new Error(
136
- `scripted: rule for approve interrupt missing 'approved' (id=${interrupt.id})`,
137
- );
138
- }
139
- return approveAnswer(interrupt.id, body.approved, body.reason);
140
- }
141
- if (!("payload" in body)) {
142
- throw new Error(`scripted: rule for custom interrupt missing 'payload' (id=${interrupt.id})`);
143
- }
144
- return { kind: "custom", id: interrupt.id, payload: body.payload };
145
- };
146
-
147
- class ScriptedAnswererImpl implements Answerer {
148
- private rules: PreparedRule[] = [];
149
- private readonly config: ScriptedAnswererConfig;
150
-
151
- constructor(config: ScriptedAnswererConfig) {
152
- this.config = config;
153
- }
154
-
155
- async prepare(): Promise<void> {
156
- const path = resolve(this.config.fixturePath);
157
- const raw = loadYaml(await readFile(path, "utf-8"));
158
- const parsed = FixtureSchema.safeParse(raw);
159
- if (!parsed.success) {
160
- throw new Error(`scripted: invalid fixture at ${path}: ${formatZodIssues(parsed.error)}`);
161
- }
162
- this.rules = parsed.data.map((rule, index) => prepareRule(rule, path, index));
163
- }
164
-
165
- async answer(interrupt: Interrupt): Promise<Answer> {
166
- const matched = this.rules.find((r) => matches(r, interrupt));
167
- if (matched) {
168
- return buildAnswer(matched.rule, interrupt);
169
- }
170
- const fallback = this.rules.find((r) => defaultAnswersKind(r.rule, interrupt.kind));
171
- if (fallback) {
172
- return buildAnswer(fallback.rule, interrupt);
173
- }
174
- throw new Error(
175
- `scripted: no rule matched interrupt (kind=${interrupt.kind}, id=${interrupt.id}) and no default provided`,
176
- );
177
- }
178
-
179
- async close(): Promise<void> {}
180
- }
181
-
182
- export const scriptedAnswerer: AnswererPlugin<ScriptedAnswererConfig> = {
183
- name: "scripted",
184
- configSchema: ScriptedAnswererConfigSchema,
185
- create: (config) => new ScriptedAnswererImpl(config),
186
- };