create-nola-lang 0.1.13 → 0.1.14
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/README.md +8 -3
- package/dist/{chunk-LUSVIOAN.js → chunk-CB26Z47Y.js} +53 -87
- package/dist/examples.d.ts +4 -2
- package/dist/examples.js +4 -2
- package/dist/flow.d.ts +8 -2
- package/dist/flow.js +15 -12
- package/dist/github.d.ts +1 -1
- package/dist/github.js +1 -1
- package/dist/index.js +1 -1
- package/dist/launch.d.ts +2 -2
- package/dist/launch.js +1 -1
- package/dist/main.js +1 -1
- package/dist/node-version.d.ts +3 -2
- package/dist/node-version.js +4 -3
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +1 -1
- package/dist/registry.d.ts +19 -10
- package/dist/registry.js +20 -5
- package/dist/scaffold.d.ts +19 -15
- package/dist/scaffold.js +63 -113
- package/package.json +1 -1
- package/skills/nola/SKILL.md +48 -16
- package/skills/nola/references/config.md +5 -5
- package/skills/nola/references/patterns.md +67 -31
- package/skills/nola/references/pitfalls.md +200 -52
- package/skills/nola/references/syntax.md +221 -122
- package/templates/empty/package.json +5 -5
- package/templates/empty/src/main.tsi +8 -0
- package/templates/empty/src/main.ts +0 -5
- package/templates/feature-extraction/README.md +0 -20
- package/templates/feature-extraction/nola.config.ts +0 -14
- package/templates/feature-extraction/nola.replay.jsonl +0 -2
- package/templates/feature-extraction/package.json +0 -22
- package/templates/feature-extraction/src/main.tsi +0 -22
- package/templates/feature-extraction/tsconfig.json +0 -12
- package/templates/function-calling/README.md +0 -21
- package/templates/function-calling/nola.config.ts +0 -14
- package/templates/function-calling/nola.replay.jsonl +0 -1
- package/templates/function-calling/package.json +0 -22
- package/templates/function-calling/src/main.tsi +0 -12
- package/templates/function-calling/src/tickets.ts +0 -17
- package/templates/function-calling/tsconfig.json +0 -12
- package/templates/typescript-interop/README.md +0 -15
- package/templates/typescript-interop/nola.config.ts +0 -14
- package/templates/typescript-interop/nola.replay.jsonl +0 -1
- package/templates/typescript-interop/package.json +0 -22
- package/templates/typescript-interop/src/main.ts +0 -8
- package/templates/typescript-interop/src/person.tsi +0 -11
- package/templates/typescript-interop/tsconfig.json +0 -12
|
@@ -16,44 +16,96 @@ plain TS.
|
|
|
16
16
|
```tsi
|
|
17
17
|
// plain
|
|
18
18
|
infer function summarize(.text: string) {
|
|
19
|
-
return ask `a one-sentence summary
|
|
19
|
+
return ask `a one-sentence summary`: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// exported
|
|
23
23
|
export infer function classify(.message: string) {
|
|
24
|
-
return ask `the category of the message
|
|
24
|
+
return ask `the category of the message`: string;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
//
|
|
28
|
-
export infer function triage
|
|
29
|
-
|
|
27
|
+
// a context statement: a bare template literal on its own line
|
|
28
|
+
export infer function triage(.ticket: string) {
|
|
29
|
+
`triage the ticket like a support lead`
|
|
30
|
+
return ask `the severity: low, medium or high`: "low" | "medium" | "high";
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
//
|
|
33
|
-
export infer function
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
// text and values — the value is spliced into your words, like a ${} hole
|
|
34
|
+
export infer function escalate(.ticket: string, oncall: string) {
|
|
35
|
+
`Page` oncall `when the ticket is an outage.`
|
|
36
|
+
const steps: string[] = [];
|
|
37
|
+
`Steps taken so far:` steps; // read at EACH ask: the loop sees the current list
|
|
38
|
+
while (steps.length < 3) steps.push(ask `the next step`: string);
|
|
39
|
+
return steps;
|
|
37
40
|
}
|
|
38
41
|
```
|
|
39
42
|
|
|
40
43
|
Rules:
|
|
41
44
|
|
|
42
|
-
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
- Context statements: any bare template literal statement in a scope body
|
|
46
|
+
(an infer body or the module body, at any depth of braces). Several are
|
|
47
|
+
legal. An ask sees the statements written BEFORE it, in its block or an
|
|
48
|
+
enclosing one — the `const .x` rule; each is read at each ask. Visibility
|
|
49
|
+
is lexical, like a `const`: a statement inside a block reaches only the
|
|
50
|
+
asks inside those braces (an ask after the block never sees it), and
|
|
51
|
+
statements never accumulate across loop passes — each ask sees every
|
|
52
|
+
statement visible at its position exactly once, rendered at that ask; only
|
|
53
|
+
the values change between passes. A value is a
|
|
54
|
+
name, a call or a bracketed literal (`[a, b]`, `{ a }`, `(a + b)`; a plain
|
|
55
|
+
number, string or `true` works too); a `[` or `(` group takes its whole
|
|
56
|
+
tail (`[a, b].filter(ok)` is one value). Parenthesize anything else: after
|
|
57
|
+
a value an operator or arrow is NOLA1020, and so is a value that ENDS with
|
|
58
|
+
a backtick (`` foo<string> `more` ``, `` new Foo `more` `` — write
|
|
59
|
+
`(foo<string>)`, `(new Foo)` or `new Foo()`). `await`, `ask` and `this`
|
|
60
|
+
cannot be values at all — assign the result to a local first
|
|
61
|
+
(`const user = this.user;`, then `` `Page` user ``): an item is a hoisted
|
|
62
|
+
function of its own, so a bare `this` is NOLA1020 and `this` in
|
|
63
|
+
parentheses or in a `${}` hole is a TypeScript error (TS2683); see "Values
|
|
64
|
+
in an instruction" for `await` and `ask`. A call intent is a value: a
|
|
65
|
+
sigil-less one (`` `Page` fn(`x`: T) ``) as it stands, a HINTED one
|
|
66
|
+
(`` fn`hint`(…) ``, an empty marker too) only in parentheses or a `${}`
|
|
67
|
+
hole — bare, its backtick reads as the next text part, so it is no call
|
|
68
|
+
intent (a typed template argument is then a syntax error at its colon, a
|
|
69
|
+
`` ..`x`: T `` one NOLA2010; plain arguments raise no error and render
|
|
70
|
+
the wrong text). An operator directly after the TEXT
|
|
71
|
+
makes no context statement at all: `` `Page ` + oncall `` is ordinary
|
|
72
|
+
string concatenation the model never sees — write `` `Page` oncall ``.
|
|
73
|
+
Values never sit side by side; whitespace between parts is kept as
|
|
74
|
+
written. LINES follow
|
|
75
|
+
JavaScript: a line that starts with a backtick, `[` or `(` continues the
|
|
76
|
+
statement; a line that starts with a NAME begins a new statement
|
|
77
|
+
(`` `analyze` `` ⏎ `foo(x)` is a statement and a call — put `foo(x)` at
|
|
78
|
+
the end of the text's line or in parentheses on its own line). Two
|
|
79
|
+
backtick-led lines in a row are ONE statement (the second line's
|
|
80
|
+
indentation stays in the text): end the first with `;` for two. The mirror
|
|
81
|
+
hazard: a CODE line starting with `[` or `(` right after a context
|
|
82
|
+
statement is taken as its value (its result lands in the prompt, and the
|
|
83
|
+
code runs at every ask) — end the statement with `;`. A backtick literal
|
|
84
|
+
between the name and the parameters (`` infer function f`…`(…) ``) is
|
|
85
|
+
reserved — NOLA1019.
|
|
86
|
+
- The MODULE body takes the same statements and `const .x` bindings — see
|
|
87
|
+
"The `ask` operator". A top-level module statement reaches the asks below
|
|
88
|
+
it and the infer functions DECLARED below it: an ask inside one renders the
|
|
89
|
+
module's `<context module="<file>">` block before the function's, whoever
|
|
90
|
+
calls it, once. A function's OWN VIEW of the module is the top-level
|
|
91
|
+
statements written above its declaration. A function declared ABOVE the
|
|
92
|
+
statement does not see it on its own (a detached `await fn()` sees only the
|
|
93
|
+
function's own view), but `ask fn()` from code that saw it — module code
|
|
94
|
+
below the statement, an infer function declared below it — renders the
|
|
95
|
+
block once with the caller's statements added. A statement inside a
|
|
96
|
+
module-level block reaches a function only through an `ask fn()` written
|
|
97
|
+
inside that block. Bindings are not carried.
|
|
98
|
+
- A context statement in a plain function, a callback, a static block or a
|
|
99
|
+
namespace body — or as the UNBRACED body of an `if`/`else`/loop/label — is
|
|
100
|
+
NOLA2017 (put braces around the body). In a file with no `ask` and no
|
|
101
|
+
infer function a lone text statement is left as written (plain JavaScript,
|
|
102
|
+
a no-op), but a statement of two or more parts makes the file use the
|
|
103
|
+
runtime and every context statement in it an item.
|
|
49
104
|
|
|
50
105
|
- Top-level function declarations only. `infer` on a method, arrow function,
|
|
51
|
-
or function expression is a
|
|
106
|
+
or function expression is a plain parse error (NOLA1001).
|
|
52
107
|
- `async infer function` is a parse error — an infer function is never `async`
|
|
53
108
|
in source; it is implicitly awaitable through `Intent`.
|
|
54
|
-
- The instruction marker is a template literal. `${expr}` holes interpolate
|
|
55
|
-
lexical values into the instruction; a `${.member}` hole makes the marker a
|
|
56
|
-
prompt TEMPLATE for the function's CONTEXT block (see "Prompt templates").
|
|
57
109
|
- `export default infer function` does NOT parse. Export by name
|
|
58
110
|
(`export infer function f(...)`) and let consumers import the name.
|
|
59
111
|
- `ask` is legal only DIRECTLY inside an infer function body or DIRECTLY in
|
|
@@ -68,7 +120,7 @@ Rules:
|
|
|
68
120
|
```tsi
|
|
69
121
|
export infer function enrich(.handle: string, fetchProfile: (h: string) => Promise<string>) {
|
|
70
122
|
const profile = await fetchProfile(handle); // ordinary promise
|
|
71
|
-
return ask `the person's job title from: ${profile}
|
|
123
|
+
return ask `the person's job title from: ${profile}`: string;
|
|
72
124
|
}
|
|
73
125
|
```
|
|
74
126
|
|
|
@@ -86,7 +138,7 @@ interface User {
|
|
|
86
138
|
}
|
|
87
139
|
|
|
88
140
|
export infer function getUser(.message: string): Intent<User> {
|
|
89
|
-
const user = ask `the user described in the message
|
|
141
|
+
const user = ask `the user described in the message`: User;
|
|
90
142
|
return user;
|
|
91
143
|
}
|
|
92
144
|
```
|
|
@@ -101,15 +153,16 @@ A parameter prefixed with ONE dot is a CONTEXT parameter: its name, type and
|
|
|
101
153
|
runtime VALUE are composed into the prompt of every `ask` in that invocation.
|
|
102
154
|
A plain parameter is an ordinary JS argument — its name and type reach the
|
|
103
155
|
LLM, its value does not. Mnemonic: one dot IN (`.name`); a template after
|
|
104
|
-
`ask` OUT — `` ..`prompt` `` spells the same request where `ask`
|
|
105
|
-
|
|
156
|
+
`ask` OUT — `` ..`prompt` `` spells the same request where neither `ask`
|
|
157
|
+
nor a call's argument list is in front of it. Writing `..name` on a
|
|
158
|
+
parameter is NOLA1013.
|
|
106
159
|
|
|
107
160
|
```tsi
|
|
108
161
|
export type Issue = { id: string; description: string };
|
|
109
162
|
|
|
110
163
|
// `issue` is visible to the LLM; `fallback` is a normal JS value only.
|
|
111
164
|
export infer function classifyIssue(.issue: Issue, fallback: string) {
|
|
112
|
-
const kind = ask `the kind of this issue
|
|
165
|
+
const kind = ask `the kind of this issue`: string;
|
|
113
166
|
return kind || fallback;
|
|
114
167
|
}
|
|
115
168
|
```
|
|
@@ -127,7 +180,7 @@ export infer function classifyIssue(.issue: Issue, fallback: string) {
|
|
|
127
180
|
|
|
128
181
|
```tsi
|
|
129
182
|
export infer function nextQuery(.question: string, .notes: string[]) {
|
|
130
|
-
return ask `the single best search query to advance the research
|
|
183
|
+
return ask `the single best search query to advance the research`: string;
|
|
131
184
|
}
|
|
132
185
|
```
|
|
133
186
|
- `const .x = …` / `let .x = …` is a contextual BINDING: context for every
|
|
@@ -137,27 +190,29 @@ export infer function nextQuery(.question: string, .notes: string[]) {
|
|
|
137
190
|
parameter (same `underivableContextType` policy), and travels to a callee
|
|
138
191
|
through `ask fn()`. At module level it gives the `<module>` scope its
|
|
139
192
|
CONTEXT block; it does NOT reach infer functions merely declared in the
|
|
140
|
-
file
|
|
193
|
+
file (a module context statement does, in the functions declared below
|
|
194
|
+
it — see "infer function"). `var .x` is NOLA1014; a pattern is NOLA1011.
|
|
141
195
|
|
|
142
196
|
```tsi
|
|
143
197
|
export infer function reply(.mail: string) {
|
|
144
198
|
const .tone = "brief, friendly";
|
|
145
199
|
const .customer: Customer = await loadCustomer(mail);
|
|
146
|
-
return ask `a reply to the mail
|
|
200
|
+
return ask `a reply to the mail`: string;
|
|
147
201
|
}
|
|
148
202
|
```
|
|
149
203
|
|
|
150
|
-
## Extractors — `` ask `instruction
|
|
204
|
+
## Extractors — `` ask `instruction`: T `` and `` ..`instruction`: T ``
|
|
151
205
|
|
|
152
206
|
An extractor is the request itself: instruction text in backticks plus an
|
|
153
|
-
optional type argument. Directly after `ask` (and after `ask with <name>`)
|
|
207
|
+
optional type argument. Directly after `ask` (and after `ask with <name>`),
|
|
208
|
+
and as a typed slot in a call's arguments (`` createTicket(`title`: string, 2) ``),
|
|
154
209
|
the backticks alone are the extractor; everywhere else it is written with two
|
|
155
210
|
leading dots so it cannot be mistaken for a string.
|
|
156
211
|
|
|
157
212
|
```tsi
|
|
158
213
|
export infer function parse(.doc: string) {
|
|
159
|
-
const id = ask `the ticket id
|
|
160
|
-
const count = ask `how many line items
|
|
214
|
+
const id = ask `the ticket id`: string; // typed
|
|
215
|
+
const count = ask `how many line items`: number;
|
|
161
216
|
const free = ask `think step by step about the document`; // untyped
|
|
162
217
|
return { id, count, free };
|
|
163
218
|
}
|
|
@@ -165,9 +220,7 @@ export infer function parse(.doc: string) {
|
|
|
165
220
|
|
|
166
221
|
- `${expr}` interpolation is legal inside the backticks and is evaluated at
|
|
167
222
|
intent-construction time. Strings splice as-is; anything else is
|
|
168
|
-
JSON-stringified. A hole
|
|
169
|
-
lexical value — it reads the extractor's prompt scope and turns the
|
|
170
|
-
backticks into a prompt template (see "Prompt templates"):
|
|
223
|
+
JSON-stringified. A hole cannot contain a Nola construct (NOLA2010):
|
|
171
224
|
|
|
172
225
|
```tsi
|
|
173
226
|
interface Person {
|
|
@@ -176,20 +229,45 @@ interface Person {
|
|
|
176
229
|
}
|
|
177
230
|
|
|
178
231
|
export infer function lookup(text: string) {
|
|
179
|
-
return ask `the person described in: ${text}
|
|
232
|
+
return ask `the person described in: ${text}`: Person;
|
|
180
233
|
}
|
|
181
234
|
```
|
|
182
235
|
|
|
183
|
-
- With no
|
|
236
|
+
- With no type, the extractor asks for free text: the wire schema is a plain
|
|
184
237
|
string and the static TS type is `any`. Give every extractor an explicit
|
|
185
|
-
|
|
186
|
-
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
the
|
|
190
|
-
``
|
|
191
|
-
|
|
192
|
-
|
|
238
|
+
type unless you deliberately want unconstrained prose.
|
|
239
|
+
- TWO SPELLINGS OF THE TYPE, one construct. `: T` — a colon GLUED to the
|
|
240
|
+
closing backtick, like an annotation — is the taught form and what every
|
|
241
|
+
example writes. `<T>` — a type argument after the backtick — is the same
|
|
242
|
+
construct: `` ask `the ticket id`<string> ``, `` ..`x`<T> ``,
|
|
243
|
+
`` fn(`x`<T>) ``, `` ask `q`<Choice<C>> `` — same AST, same emit, same
|
|
244
|
+
definition, same ledger entries; legal everywhere `: T` is. Rules for the
|
|
245
|
+
colon: a colon after whitespace is never the extractor's (so
|
|
246
|
+
`` cond ? ask `p` : fallback `` stays a ternary over an untyped ask, and
|
|
247
|
+
`` cond ? ask `p`: number : fallback `` asks for a number); the type starts
|
|
248
|
+
on the colon's line (NOLA1018 otherwise); a type NAME followed by `.`
|
|
249
|
+
continues the type (`` `p`: User.withRetry(2) `` is the type
|
|
250
|
+
`User.withRetry` called with 2) — chain intent methods after a
|
|
251
|
+
parenthesized extractor (`` ask (..`p`: User).withRetry(2) ``), a
|
|
252
|
+
delimited type (`string[]`, `Array<User>`, `{…}`, `(User)`) or `<T>`;
|
|
253
|
+
`<` after a type name opens type arguments with either spelling.
|
|
254
|
+
- The `..` is implied in two places: directly after `ask` (the template is
|
|
255
|
+
the operand's first token) and in a call's SLOTS — a typed template that
|
|
256
|
+
starts an argument, or a property value / element of a plain object or
|
|
257
|
+
array literal written in the arguments: `` fn(`x`: T) ``,
|
|
258
|
+
`` api.save({ note: `x`: T }) ``, `` fn([`x`: T]) ``. Everywhere else it is
|
|
259
|
+
required: `` const i = ..`x`: T ``, `` fn(cond ? ..`x`: T : y) ``,
|
|
260
|
+
`` fn(...[..`x`: T]) ``, `` new Foo(..`x`: T) ``,
|
|
261
|
+
`` ask (..`x`: T).withRetry(2) `` (parenthesized: the template is no longer
|
|
262
|
+
a first token — `` fn((`x`: T)) `` is a syntax error too). A `` `x`<T> ``
|
|
263
|
+
outside those places is NOLA2014; a `` `x`: T `` there is a plain syntax
|
|
264
|
+
error. An UNTYPED template in a slot is a plain string argument
|
|
265
|
+
(`` log(`done`) `` is an ordinary call), and in a slot `<` starts type
|
|
266
|
+
arguments only when `<…>` reads as such (`` fn(`a` < b) `` stays a
|
|
267
|
+
comparison). `` ask ..`x`: T `` and `` fn(..`x`: T) `` are still legal and
|
|
268
|
+
lower identically. The SPACE is mandatory after `ask`: `` ask`x` ``
|
|
269
|
+
(backtick glued to `ask`, or to the name after `ask with`) is NOLA1017 —
|
|
270
|
+
it reads as a tagged template.
|
|
193
271
|
- DECISION TYPES (intrinsic, no import): `Choice<{ billing: "Payments";
|
|
194
272
|
sales: null }>` (or `Choice<"a" | "b">`, 2–255 labels; number labels too,
|
|
195
273
|
alone or mixed — `Choice<1 | 2 | 3>` / `Choice<{ 1: "Low"; 2: "High" }>` /
|
|
@@ -205,12 +283,12 @@ export infer function lookup(text: string) {
|
|
|
205
283
|
at 0.5. RULE: adding criteria changes what the property evaluates to. A
|
|
206
284
|
type containing a decision type needs a DECISION model (`typesafe()`,
|
|
207
285
|
`mockProvider(replies, { decisions: true })` in tests) — on a chat model it
|
|
208
|
-
is NOLA3018 before the network.
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
-
|
|
286
|
+
is NOLA3018 before the network. There is NO `..choice` / `..scale` /
|
|
287
|
+
`..prob` sugar (retired 2026-09-23; an identifier after `..` is NOLA1005):
|
|
288
|
+
write the type long-hand — `` ask `Which team?`: Choice<{ billing:
|
|
289
|
+
"Payments"; sales: null }> ``, `` ask `How bad?`: Scale<["low", "high"]> ``,
|
|
290
|
+
`` ask `Urgent?`: Prob ``; malformed criteria are NOLA2015.
|
|
291
|
+
- The type accepts whatever resolves to a JSON shape: scalars, `Date`, arrays,
|
|
214
292
|
tuples, object literals, aliases/interfaces (same file, another file, a
|
|
215
293
|
package; `extends`, intersections, `Partial`/`Pick`/`Omit`, instantiated
|
|
216
294
|
generics), string-literal unions and string enums, object and nullable
|
|
@@ -230,7 +308,7 @@ export interface Conclusion {
|
|
|
230
308
|
(infer body or module body):
|
|
231
309
|
|
|
232
310
|
```tsi
|
|
233
|
-
export const nameIntent = ..`the user's full name
|
|
311
|
+
export const nameIntent = ..`the user's full name`: string; // legal, inert
|
|
234
312
|
```
|
|
235
313
|
|
|
236
314
|
## The `ask` operator
|
|
@@ -245,7 +323,7 @@ import { getUserById } from "./users.tsi";
|
|
|
245
323
|
type User = { name: string };
|
|
246
324
|
|
|
247
325
|
export infer function report(.text: string) {
|
|
248
|
-
const user = ask `the user named in the text
|
|
326
|
+
const user = ask `the user named in the text`: User; // extractor
|
|
249
327
|
const record = ask getUserById(user.name); // another infer function
|
|
250
328
|
return record;
|
|
251
329
|
}
|
|
@@ -255,8 +333,8 @@ export infer function report(.text: string) {
|
|
|
255
333
|
|
|
256
334
|
```tsi
|
|
257
335
|
export infer function summarize(.text: string) {
|
|
258
|
-
const draft = ask with fast `a rough summary
|
|
259
|
-
const final = ask with careful `a polished summary of: ${draft}
|
|
336
|
+
const draft = ask with fast `a rough summary`: string;
|
|
337
|
+
const final = ask with careful `a polished summary of: ${draft}`: string;
|
|
260
338
|
return final;
|
|
261
339
|
}
|
|
262
340
|
```
|
|
@@ -282,8 +360,9 @@ Three spellings:
|
|
|
282
360
|
declare function createTicket(title: string, priority: number): Promise<string>;
|
|
283
361
|
|
|
284
362
|
export infer function file(.request: string) {
|
|
285
|
-
// 1. sigil-less — a plain call whose arguments contain an extractor
|
|
286
|
-
|
|
363
|
+
// 1. sigil-less — a plain call whose arguments contain an extractor; the
|
|
364
|
+
// slot's `..` is implied (`..`a short ticket title`: string` still works)
|
|
365
|
+
const a = ask createTicket(`a short ticket title`: string, 2);
|
|
287
366
|
|
|
288
367
|
// 2. empty marker — identical lowering; the only spelling for a call
|
|
289
368
|
// intent whose arguments are all plain
|
|
@@ -291,8 +370,8 @@ export infer function file(.request: string) {
|
|
|
291
370
|
|
|
292
371
|
// 3. hint marker — the ONLY carrier of instruction text for the call
|
|
293
372
|
const c = ask createTicket`file the ticket the customer asked for`(
|
|
294
|
-
|
|
295
|
-
|
|
373
|
+
`a short ticket title`: string,
|
|
374
|
+
`priority 1-5, 1 is most urgent`: number,
|
|
296
375
|
);
|
|
297
376
|
|
|
298
377
|
return { a, b, c };
|
|
@@ -304,33 +383,36 @@ Detection rule for the sigil-less form — BOTH must hold:
|
|
|
304
383
|
- the callee is an `Identifier` or a `MemberExpression` (any nesting, computed
|
|
305
384
|
included), and
|
|
306
385
|
- at least one well-formed extractor appears in a slot position: a direct
|
|
307
|
-
argument, or nested at any depth inside plain object/array literals.
|
|
386
|
+
argument, or nested at any depth inside plain object/array literals. In
|
|
387
|
+
exactly those positions a typed template is an extractor without the `..`;
|
|
388
|
+
an untyped one is a plain string argument.
|
|
308
389
|
|
|
309
390
|
```tsi
|
|
310
391
|
declare const api: { save(order: { qty: number; note: string }): Promise<string> };
|
|
311
392
|
|
|
312
393
|
export infer function place(.request: string) {
|
|
313
394
|
// member callee + extractor nested in an object literal → call intent
|
|
314
|
-
return ask api.save({ qty: 1, note:
|
|
395
|
+
return ask api.save({ qty: 1, note: `a one-line note for the warehouse`: string });
|
|
315
396
|
}
|
|
316
397
|
```
|
|
317
398
|
|
|
318
|
-
These stay PLAIN calls (the extractor is just a value argument
|
|
319
|
-
inside a ternary, logical expression, spread element
|
|
320
|
-
a nested call (in `` outer(inner(
|
|
399
|
+
These stay PLAIN calls (the extractor is just a value argument, and needs its
|
|
400
|
+
`..` there): an extractor inside a ternary, logical expression, spread element
|
|
401
|
+
or template substitution; a nested call (in `` outer(inner(`x`: T)) `` the
|
|
402
|
+
INNER call is the intent and
|
|
321
403
|
`outer` receives an `Askable`); `new Foo(...)`, `super(...)`, `import(...)`,
|
|
322
404
|
optional calls (`fn?.(...)`, `a?.b(...)`); and exotic callees (`getFn()(...)`,
|
|
323
|
-
IIFEs) — use the
|
|
405
|
+
IIFEs) — use the hint form `` fn`hint`(…) `` if you want a call intent on one of those.
|
|
324
406
|
|
|
325
407
|
Parenthesizing an extractor does NOT opt out. To pass an intent as a plain
|
|
326
408
|
value, bind it to a variable first:
|
|
327
409
|
|
|
328
410
|
```tsi
|
|
329
|
-
const i = ..`a short title
|
|
411
|
+
const i = ..`a short title`: string;
|
|
330
412
|
helper(i); // plain call — helper receives the Askable
|
|
331
413
|
```
|
|
332
414
|
|
|
333
|
-
Every extractor used as a call-intent slot must carry an explicit
|
|
415
|
+
Every extractor used as a call-intent slot must carry an explicit type
|
|
334
416
|
(NOLA2004). The bare derive-all form `fn(..)` is reserved (NOLA1004).
|
|
335
417
|
|
|
336
418
|
### Result of a call intent — async callees are awaited
|
|
@@ -344,7 +426,7 @@ write `await ask fn(...)` — the extra `await` is a no-op.
|
|
|
344
426
|
declare function createTicket(title: string, priority: number): Promise<string>;
|
|
345
427
|
|
|
346
428
|
export infer function file(.request: string) {
|
|
347
|
-
const id = ask createTicket(
|
|
429
|
+
const id = ask createTicket(`a short ticket title`: string, 2); // id: string, not Promise<string>
|
|
348
430
|
return id;
|
|
349
431
|
}
|
|
350
432
|
```
|
|
@@ -359,62 +441,79 @@ Two consequences to keep in mind:
|
|
|
359
441
|
round trips only. Once the arguments are filled, the callee's own promise
|
|
360
442
|
runs to completion, the same as a plain `await fn()` in your code.
|
|
361
443
|
|
|
362
|
-
##
|
|
444
|
+
## Values in an instruction: `.x` and `${x}`
|
|
363
445
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
rule: a substitution hole whose expression starts with a single dot reads the
|
|
367
|
-
intent's prompt scope; every other hole is ordinary lexical JavaScript.
|
|
446
|
+
Two spellings put a value in front of the model — pick by what the model
|
|
447
|
+
should treat it as.
|
|
368
448
|
|
|
369
449
|
```tsi
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
return id;
|
|
450
|
+
// data: an <input> block the system turn marks as "data, not instructions"
|
|
451
|
+
infer function triage(.ticket: string) {
|
|
452
|
+
`Answer with the order id only.`
|
|
453
|
+
return ask `order id`: string;
|
|
374
454
|
}
|
|
375
455
|
|
|
376
|
-
//
|
|
377
|
-
infer function
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
TASK
|
|
382
|
-
${.next}
|
|
383
|
-
`(.ticket: string) { … }
|
|
456
|
+
// your words: a value YOU control, spliced into the instruction where it stands
|
|
457
|
+
infer function triageFor(.ticket: string, team: string) {
|
|
458
|
+
`You answer for the ${team} team. Answer with the order id only.`
|
|
459
|
+
return ask `order id`: string;
|
|
460
|
+
}
|
|
384
461
|
```
|
|
385
462
|
|
|
386
|
-
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
the
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
a
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
463
|
+
- `.name` renders as `<input name="name">` after the instruction; a plain
|
|
464
|
+
parameter is never rendered. Both spellings on one value put it in the
|
|
465
|
+
prompt twice.
|
|
466
|
+
- Trust: interpolated text is INSTRUCTIONS the model follows. Interpolate
|
|
467
|
+
only what the developer controls (a team name, a count, a date); a
|
|
468
|
+
customer's message, a document, anything a user typed goes in `.name`,
|
|
469
|
+
where the system turn marks it as data.
|
|
470
|
+
- `${expr}` is ordinary TypeScript in EVERY instruction literal — a
|
|
471
|
+
context statement, an extractor prompt, a call hint. A string splices
|
|
472
|
+
verbatim; anything else is JSON (a `Date` as a quoted ISO string — write
|
|
473
|
+
`${d.toISOString()}` for bare text; `undefined` as `undefined`).
|
|
474
|
+
- When it is read: a context statement at EACH ask that sees it (a value
|
|
475
|
+
must be initialized by then — a `let` or `const` declared after the ask
|
|
476
|
+
is a ReferenceError at that ask); an extractor's prompt and a call hint when
|
|
477
|
+
the ask expression runs. A value after text in a context statement is the
|
|
478
|
+
same as a `${}` hole: `` `Page` oncall `…` `` ≡ `` `Page ${oncall} …` ``.
|
|
479
|
+
- A custom format is a function call in a hole (`${table(a, b)}`); a value
|
|
480
|
+
kept out of the prompt is a dotless parameter.
|
|
481
|
+
- Errors: a Nola construct (`..`, `ask`) in a context statement (a
|
|
482
|
+
parenthesized value, a hole) or in a call hint's hole is NOLA2010 — bare
|
|
483
|
+
right after a statement's text it is NOLA1020. A call intent is a legal
|
|
484
|
+
value in a context statement — it renders as its callee and arguments; a
|
|
485
|
+
HINTED one (`` fn`hint`(…) ``) must be parenthesized or written in a `${}`
|
|
486
|
+
hole (bare, the hint's backtick reads as the next text part: an extractor
|
|
487
|
+
argument is then NOLA2010). A bare `await` or `this` right after a statement's
|
|
488
|
+
text is NOLA1020; anywhere else in a value or hole it is a TypeScript
|
|
489
|
+
error (an item is read at each ask, outside the async body, in a function
|
|
490
|
+
of its own — TS1308 for `await`, TS2683 for `this`): compute the value
|
|
491
|
+
first, or assign `this` to a local. There is no
|
|
492
|
+
`${.member}` prompt scope: a dot cannot start a hole's expression (an
|
|
493
|
+
ordinary syntax error).
|
|
494
|
+
- Editor: completion, hover and TypeScript errors work inside `${…}`.
|
|
495
|
+
|
|
496
|
+
## The prompt
|
|
497
|
+
|
|
498
|
+
What reaches the model is the developer's text in four utility tags —
|
|
499
|
+
nothing else of Nola's is in the user turn:
|
|
500
|
+
|
|
501
|
+
- `<context function="f">` / `<context module="path">`: one scope — its
|
|
502
|
+
context statements (each starting on its own line), then one
|
|
503
|
+
`<input name="x">` per contextual param and `const .x` binding (a string
|
|
504
|
+
verbatim, anything else as JSON). Plain params are NOT rendered. Outermost
|
|
505
|
+
caller first, asking scope last.
|
|
506
|
+
- `<task>` last: the extractor's instruction; a call intent is
|
|
507
|
+
`<task call="fn">hint</task>` (self-closing without a hint).
|
|
508
|
+
- `<correction>` on the retry, after the rejected reply.
|
|
509
|
+
- The schema is NEVER in the prompt: it rides the provider's structured
|
|
510
|
+
output (`structuredOutputs: false` on a vendor factory puts it in the
|
|
511
|
+
system turn as `<schema>` for servers that cannot enforce it).
|
|
512
|
+
- The system turn is four constant sentences (`DEFAULT_SYSTEM`, exported
|
|
513
|
+
from `@nola-lang/providers`); `system.message` in the config REPLACES it.
|
|
514
|
+
- Every provider implements `infer(req)` over `req.intent` (the ask as
|
|
515
|
+
data) and renders it itself — `renderPrompt(req.intent)` is the default.
|
|
516
|
+
A model that still implements `complete(req)` is NOLA3003.
|
|
418
517
|
|
|
419
518
|
## Intent methods
|
|
420
519
|
|
|
@@ -422,10 +521,10 @@ Every intent (extractor, call intent, infer-function result) accepts:
|
|
|
422
521
|
|
|
423
522
|
```tsi
|
|
424
523
|
export infer function tuned(.text: string) {
|
|
425
|
-
const a = ask (..`the title
|
|
426
|
-
const b = ask (..`the body
|
|
427
|
-
const c = ask (..`a creative tagline
|
|
428
|
-
const d = ask (..`a one-paragraph summary
|
|
524
|
+
const a = ask (..`the title`: string).withRetry(2);
|
|
525
|
+
const b = ask (..`the body`: string).withModel("careful");
|
|
526
|
+
const c = ask (..`a creative tagline`: string).withParams({ temperature: 0.9, maxOutputTokens: 200 });
|
|
527
|
+
const d = ask (..`a one-paragraph summary`: string).withTimeout(10_000);
|
|
429
528
|
return { a, b, c, d };
|
|
430
529
|
}
|
|
431
530
|
```
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "empty",
|
|
3
3
|
"version": "0.0.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"start": "nola run src/main.
|
|
7
|
+
"start": "nola run src/main.tsi",
|
|
8
8
|
"build": "nola build",
|
|
9
9
|
"check": "nola check"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@nola-lang/providers": "
|
|
13
|
-
"@nola-lang/runtime": "
|
|
12
|
+
"@nola-lang/providers": "*",
|
|
13
|
+
"@nola-lang/runtime": "*"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
|
-
"nola-lang": "
|
|
16
|
+
"nola-lang": "*",
|
|
17
17
|
"typescript": "^5.6.0"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// This file is a Nola module: TypeScript plus `ask`. Write your program here.
|
|
2
|
+
// An ask is legal at the top level — nothing to declare or call first:
|
|
3
|
+
//
|
|
4
|
+
// const .text = "The demo went great and the customer signed on the spot.";
|
|
5
|
+
// const mood = ask `the mood of the text`: "happy" | "sad" | "neutral";
|
|
6
|
+
//
|
|
7
|
+
// Language reference: https://nola.sh/docs/language/ask/
|
|
8
|
+
console.log("Hello from Nola");
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# __NAME__
|
|
2
|
-
|
|
3
|
-
A [Nola](https://github.com/nola-lang/nola) project. Nola is a TypeScript
|
|
4
|
-
superset (`.tsi`) where `ask` turns a prompt into a typed value. This
|
|
5
|
-
project is one file, `src/main.tsi`: a first-line instruction, `const .x`
|
|
6
|
-
context bindings, and `ask` at the top level — the file is the program.
|
|
7
|
-
|
|
8
|
-
```bash
|
|
9
|
-
npm install
|
|
10
|
-
npm start # runs src/main.tsi — __START_NOTE__
|
|
11
|
-
npm run check # type-checks the .tsi file
|
|
12
|
-
npm run build # compiles to plain JS in dist/
|
|
13
|
-
npx nola-lang console # traces every ask in your browser (it prints the config line to add)
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
__PROVIDER_NOTE__
|
|
17
|
-
|
|
18
|
-
When a script outgrows one file, move the asks into an `infer function`
|
|
19
|
-
and call it from plain TypeScript — `npm create nola -- --template typescript-interop`
|
|
20
|
-
lays down that shape.
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { replay } from "@nola-lang/providers";
|
|
2
|
-
import { defineConfig } from "@nola-lang/runtime";
|
|
3
|
-
|
|
4
|
-
export default defineConfig({
|
|
5
|
-
// This project runs offline: answers replay from the committed ledger
|
|
6
|
-
// (nola.replay.jsonl), so the first `npm start` needs no API key. The
|
|
7
|
-
// ledger is keyed by the exact prompt — once you edit src/main.tsi or add
|
|
8
|
-
// asks, switch to a real model:
|
|
9
|
-
// model: "nola", // Nola serves inference; `npx nola-lang key` writes NOLA_API_KEY (25 free runs)
|
|
10
|
-
// or bring your own:
|
|
11
|
-
// import { openai } from "@nola-lang/providers";
|
|
12
|
-
// model: openai("gpt-5-mini"), // reads OPENAI_API_KEY
|
|
13
|
-
model: replay("./nola.replay.jsonl"),
|
|
14
|
-
});
|