tjs-lang 0.8.2 → 0.8.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.
- package/CLAUDE.md +5 -2
- package/demo/autocomplete.test.ts +37 -0
- package/demo/docs.json +701 -11
- package/demo/src/autocomplete.ts +40 -2
- package/demo/src/introspection-bridge.ts +140 -0
- package/demo/src/introspection-doc.test.ts +63 -0
- package/demo/src/playground-shared.ts +53 -0
- package/demo/src/tjs-playground.ts +21 -0
- package/dist/experiments/predicates/css.predicates.d.ts +13 -0
- package/dist/experiments/predicates/verify.d.ts +39 -0
- package/dist/index.js +101 -97
- package/dist/index.js.map +4 -4
- package/dist/src/lang/index.d.ts +2 -0
- package/dist/src/lang/predicate-schema.d.ts +39 -0
- package/dist/src/lang/predicate.d.ts +103 -0
- package/dist/src/lang/transpiler.d.ts +2 -0
- package/dist/src/vm/runtime.d.ts +22 -0
- package/dist/tjs-eval.js +29 -29
- package/dist/tjs-eval.js.map +3 -3
- package/dist/tjs-from-ts.js +1 -1
- package/dist/tjs-from-ts.js.map +2 -2
- package/dist/tjs-lang.js +86 -82
- package/dist/tjs-lang.js.map +4 -4
- package/dist/tjs-vm.js +45 -45
- package/dist/tjs-vm.js.map +3 -3
- package/editors/codemirror/ajs-language.ts +130 -44
- package/editors/codemirror/completion-source.test.ts +114 -0
- package/editors/introspect-value.test.ts +61 -0
- package/editors/introspect-value.ts +86 -0
- package/editors/scope-symbols.test.ts +113 -0
- package/editors/scope-symbols.ts +173 -0
- package/package.json +2 -1
- package/src/lang/index.ts +22 -0
- package/src/lang/predicate-schema.test.ts +97 -0
- package/src/lang/predicate-schema.ts +168 -0
- package/src/lang/predicate.test.ts +184 -0
- package/src/lang/predicate.ts +550 -0
- package/src/lang/runtime.test.ts +46 -0
- package/src/lang/suggest.test.ts +84 -0
- package/src/lang/transpiler.ts +26 -0
- package/src/runtime.test.ts +6 -6
- package/src/vm/atom-effects.test.ts +72 -0
- package/src/vm/atoms/batteries.ts +12 -0
- package/src/vm/equality.test.ts +59 -0
- package/src/vm/runtime.ts +77 -59
package/src/lang/transpiler.ts
CHANGED
|
@@ -24,6 +24,32 @@ export {
|
|
|
24
24
|
type SourceKind,
|
|
25
25
|
} from './dialect'
|
|
26
26
|
|
|
27
|
+
// Predicate-safety: verify a cluster of pure, synchronous, composable predicates
|
|
28
|
+
export {
|
|
29
|
+
verifyPredicate,
|
|
30
|
+
compilePredicate,
|
|
31
|
+
suggest,
|
|
32
|
+
effectfulFromAtoms,
|
|
33
|
+
formatPredicateDiagnostics,
|
|
34
|
+
PredicateFuelExhausted,
|
|
35
|
+
type PredicateDiagnostic,
|
|
36
|
+
type PredicateVerifyResult,
|
|
37
|
+
type VerifyPredicateOptions,
|
|
38
|
+
type CompilePredicateOptions,
|
|
39
|
+
type Suggestion,
|
|
40
|
+
type SuggestOptions,
|
|
41
|
+
} from './predicate'
|
|
42
|
+
|
|
43
|
+
// Predicate-aware JSON-Schema: the `$predicate` keyword (computational types)
|
|
44
|
+
export {
|
|
45
|
+
compilePredicateSchema,
|
|
46
|
+
validatePredicateSchema,
|
|
47
|
+
type PredicateSchema,
|
|
48
|
+
type SchemaError,
|
|
49
|
+
type SchemaValidationResult,
|
|
50
|
+
type PredicateSchemaOptions,
|
|
51
|
+
} from './predicate-schema'
|
|
52
|
+
|
|
27
53
|
// AST emitter
|
|
28
54
|
export { transformFunction } from './emitters/ast'
|
|
29
55
|
|
package/src/runtime.test.ts
CHANGED
|
@@ -596,8 +596,8 @@ describe('Edge Cases', () => {
|
|
|
596
596
|
expect(result.result.res).toEqual({ value: 42 })
|
|
597
597
|
})
|
|
598
598
|
|
|
599
|
-
describe('
|
|
600
|
-
it('==
|
|
599
|
+
describe('footgun-free equality in expressions (== is NOT structural)', () => {
|
|
600
|
+
it('distinct arrays are NOT equal (== matches TJS, not structural)', async () => {
|
|
601
601
|
const ast = {
|
|
602
602
|
op: 'seq',
|
|
603
603
|
steps: [
|
|
@@ -621,10 +621,10 @@ describe('Edge Cases', () => {
|
|
|
621
621
|
],
|
|
622
622
|
} as any
|
|
623
623
|
const result = await vm.run(ast, {})
|
|
624
|
-
expect(result.result.res).toBe(
|
|
624
|
+
expect(result.result.res).toBe(false) // distinct arrays — NOT structural
|
|
625
625
|
})
|
|
626
626
|
|
|
627
|
-
it('==
|
|
627
|
+
it('distinct objects are NOT equal (== matches TJS, not structural)', async () => {
|
|
628
628
|
const ast = {
|
|
629
629
|
op: 'seq',
|
|
630
630
|
steps: [
|
|
@@ -648,7 +648,7 @@ describe('Edge Cases', () => {
|
|
|
648
648
|
],
|
|
649
649
|
} as any
|
|
650
650
|
const result = await vm.run(ast, {})
|
|
651
|
-
expect(result.result.res).toBe(
|
|
651
|
+
expect(result.result.res).toBe(false) // distinct objects — NOT structural
|
|
652
652
|
})
|
|
653
653
|
|
|
654
654
|
it('== does not coerce types', async () => {
|
|
@@ -678,7 +678,7 @@ describe('Edge Cases', () => {
|
|
|
678
678
|
expect(result.result.res).toBe(false) // no coercion
|
|
679
679
|
})
|
|
680
680
|
|
|
681
|
-
it('!= returns true for
|
|
681
|
+
it('!= returns true for distinct objects', async () => {
|
|
682
682
|
const ast = {
|
|
683
683
|
op: 'seq',
|
|
684
684
|
steps: [
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test'
|
|
2
|
+
import { coreAtoms, EFFECTFUL_CORE_OPS, type AtomDef } from './runtime'
|
|
3
|
+
import { batteryAtoms } from './atoms/index'
|
|
4
|
+
|
|
5
|
+
const allAtoms = { ...coreAtoms, ...batteryAtoms } as Record<string, AtomDef>
|
|
6
|
+
|
|
7
|
+
describe('atom effects classification (predicate-safety keystone)', () => {
|
|
8
|
+
it('every atom is classified pure | io', () => {
|
|
9
|
+
for (const [op, atom] of Object.entries(allAtoms)) {
|
|
10
|
+
expect(atom.effects, `${op} has no effects tag`).toMatch(/^(pure|io)$/)
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('the declared effectful core ops are all tagged io', () => {
|
|
15
|
+
for (const op of EFFECTFUL_CORE_OPS) {
|
|
16
|
+
expect(coreAtoms[op as keyof typeof coreAtoms]?.effects, op).toBe('io')
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('every battery atom is io (network calls)', () => {
|
|
21
|
+
for (const atom of Object.values(batteryAtoms) as AtomDef[]) {
|
|
22
|
+
expect(atom.effects, atom.op).toBe('io')
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('the pure substrate a predicate relies on is tagged pure', () => {
|
|
27
|
+
// data / control-flow atoms predicates compose from
|
|
28
|
+
const pure = [
|
|
29
|
+
'map',
|
|
30
|
+
'filter',
|
|
31
|
+
'reduce',
|
|
32
|
+
'find',
|
|
33
|
+
'len',
|
|
34
|
+
'split',
|
|
35
|
+
'join',
|
|
36
|
+
'keys',
|
|
37
|
+
'pick',
|
|
38
|
+
'omit',
|
|
39
|
+
'merge',
|
|
40
|
+
'regexMatch',
|
|
41
|
+
'jsonParse',
|
|
42
|
+
'callLocal',
|
|
43
|
+
'if',
|
|
44
|
+
'while',
|
|
45
|
+
'return',
|
|
46
|
+
'scope',
|
|
47
|
+
]
|
|
48
|
+
for (const op of pure) {
|
|
49
|
+
expect(coreAtoms[op as keyof typeof coreAtoms]?.effects, op).toBe('pure')
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('the IO atoms are exactly the ones that may not appear in a predicate', () => {
|
|
54
|
+
const io = Object.values(allAtoms)
|
|
55
|
+
.filter((a) => a.effects === 'io')
|
|
56
|
+
.map((a) => a.op)
|
|
57
|
+
// sanity: the capability-touching atoms are all present
|
|
58
|
+
expect(io).toEqual(
|
|
59
|
+
expect.arrayContaining([
|
|
60
|
+
'httpFetch',
|
|
61
|
+
'storeGet',
|
|
62
|
+
'llmPredict',
|
|
63
|
+
'agentRun',
|
|
64
|
+
'runCode',
|
|
65
|
+
'llmVision',
|
|
66
|
+
])
|
|
67
|
+
)
|
|
68
|
+
// and none of the pure data ops leaked in
|
|
69
|
+
expect(io).not.toContain('map')
|
|
70
|
+
expect(io).not.toContain('jsonParse')
|
|
71
|
+
})
|
|
72
|
+
})
|
|
@@ -229,3 +229,15 @@ export const llmVision = defineAtom(
|
|
|
229
229
|
},
|
|
230
230
|
{ docs: 'Analyze images using a vision model', timeoutMs: 120000, cost: 150 }
|
|
231
231
|
)
|
|
232
|
+
|
|
233
|
+
// Every battery atom is IO (embedding/LLM/vector-store network calls).
|
|
234
|
+
for (const atom of [
|
|
235
|
+
storeVectorize,
|
|
236
|
+
storeCreateCollection,
|
|
237
|
+
storeVectorAdd,
|
|
238
|
+
storeSearch,
|
|
239
|
+
llmPredictBattery,
|
|
240
|
+
llmVision,
|
|
241
|
+
]) {
|
|
242
|
+
atom.effects = 'io'
|
|
243
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect } from 'bun:test'
|
|
2
|
+
import { evaluateExpr } from './runtime'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* AJS `==`/`!=` must match TJS `==` (Eq): footgun-free `===`, NOT structural.
|
|
6
|
+
* This used to do deep structural comparison in the VM — an early, unconsidered
|
|
7
|
+
* divergence from TJS `==` (so `[1,2] == [1,2]` was `true` in AJS but `false` in
|
|
8
|
+
* TJS). Now consistent: distinct objects/arrays are distinct; structural
|
|
9
|
+
* equality is an explicit operation, never `==`.
|
|
10
|
+
*/
|
|
11
|
+
const lit = (value: unknown) => ({ $expr: 'literal' as const, value })
|
|
12
|
+
const evalEq = (op: '==' | '!=', a: unknown, b: unknown) =>
|
|
13
|
+
evaluateExpr(
|
|
14
|
+
{ $expr: 'binary', op, left: lit(a), right: lit(b) } as any,
|
|
15
|
+
{ state: {}, args: {} } as any
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
describe('AJS == / != — footgun-free, NOT structural (consistent with TJS)', () => {
|
|
19
|
+
it('distinct arrays/objects are NOT equal (the divergence fix)', () => {
|
|
20
|
+
expect(evalEq('==', [1, 2], [1, 2])).toBe(false)
|
|
21
|
+
expect(evalEq('==', { a: 1 }, { a: 1 })).toBe(false)
|
|
22
|
+
expect(evalEq('!=', [1, 2], [1, 2])).toBe(true)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('identity still holds for the same reference', () => {
|
|
26
|
+
const shared = { a: 1 }
|
|
27
|
+
expect(evalEq('==', shared, shared)).toBe(true)
|
|
28
|
+
const arr = [1, 2]
|
|
29
|
+
expect(evalEq('==', arr, arr)).toBe(true)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('no type coercion', () => {
|
|
33
|
+
expect(evalEq('==', '5', 5)).toBe(false)
|
|
34
|
+
expect(evalEq('==', '', false)).toBe(false)
|
|
35
|
+
expect(evalEq('==', 0, false)).toBe(false)
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('unwraps boxed primitives', () => {
|
|
39
|
+
expect(evalEq('==', new Boolean(false) as any, false)).toBe(true)
|
|
40
|
+
expect(evalEq('==', new Number(5) as any, 5)).toBe(true)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('null/undefined equal; NaN equal to itself', () => {
|
|
44
|
+
expect(evalEq('==', null, undefined)).toBe(true)
|
|
45
|
+
expect(evalEq('==', NaN, NaN)).toBe(true)
|
|
46
|
+
expect(evalEq('==', null, 0)).toBe(false)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('scalars compare as expected', () => {
|
|
50
|
+
expect(evalEq('==', 1, 1)).toBe(true)
|
|
51
|
+
expect(evalEq('==', 'x', 'x')).toBe(true)
|
|
52
|
+
expect(evalEq('!=', 1, 2)).toBe(true)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('=== / !== remain strict identity (unchanged)', () => {
|
|
56
|
+
expect(evalEq('===' as any, [1], [1])).toBe(false)
|
|
57
|
+
expect(evalEq('===' as any, 5, 5)).toBe(true)
|
|
58
|
+
})
|
|
59
|
+
})
|
package/src/vm/runtime.ts
CHANGED
|
@@ -1,69 +1,36 @@
|
|
|
1
1
|
import { s, validate, filter as schemaFilter } from 'tosijs-schema'
|
|
2
2
|
|
|
3
|
-
/** Well-known symbol for custom equality (matches src/lang/runtime.ts) */
|
|
4
|
-
const tjsEquals = Symbol.for('tjs.equals')
|
|
5
|
-
|
|
6
3
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* AJS `==`/`!=` equality — **footgun-free `===`**, consistent with TJS `Eq`
|
|
5
|
+
* (`src/lang/runtime.ts`). NOT structural: it unwraps boxed primitives, treats
|
|
6
|
+
* `null`/`undefined` as equal and `NaN` as equal to itself, but does NOT coerce
|
|
7
|
+
* across types, and distinct objects/arrays are distinct (`[1,2] != [1,2]`).
|
|
8
|
+
*
|
|
9
|
+
* (Originally the VM did deep structural comparison here — an early, unconsidered
|
|
10
|
+
* divergence from TJS `==`. It was also a SECURITY hole: a single `==` node costs
|
|
11
|
+
* a flat EXPR_FUEL_COST, but the structural walk charged nothing per element, so
|
|
12
|
+
* `==` on an attacker-controlled large structure was an unbounded-work / fuel-
|
|
13
|
+
* bypass DoS — exactly what AJS exists to prevent. `eqValue` is O(1).
|
|
14
|
+
*
|
|
15
|
+
* Structural equality is an explicit operation: in TJS it's the `Is`/`IsNot`
|
|
16
|
+
* function; AJS can grow an `Is` atom if/when needed — but it MUST be fuel-metered
|
|
17
|
+
* per element compared, or it reintroduces the same DoS.)
|
|
9
18
|
*/
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
a !== null &&
|
|
14
|
-
typeof a === 'object' &&
|
|
15
|
-
typeof (a as any)[tjsEquals] === 'function'
|
|
16
|
-
) {
|
|
17
|
-
return (a as any)[tjsEquals](b)
|
|
19
|
+
function eqValue(a: unknown, b: unknown): boolean {
|
|
20
|
+
if (a instanceof String || a instanceof Number || a instanceof Boolean) {
|
|
21
|
+
a = a.valueOf()
|
|
18
22
|
}
|
|
19
|
-
if (
|
|
20
|
-
b
|
|
21
|
-
typeof b === 'object' &&
|
|
22
|
-
typeof (b as any)[tjsEquals] === 'function'
|
|
23
|
-
) {
|
|
24
|
-
return (b as any)[tjsEquals](a)
|
|
23
|
+
if (b instanceof String || b instanceof Number || b instanceof Boolean) {
|
|
24
|
+
b = b.valueOf()
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
// .Equals method
|
|
28
|
-
if (
|
|
29
|
-
a !== null &&
|
|
30
|
-
typeof a === 'object' &&
|
|
31
|
-
typeof (a as any).Equals === 'function'
|
|
32
|
-
) {
|
|
33
|
-
return (a as any).Equals(b)
|
|
34
|
-
}
|
|
35
|
-
if (
|
|
36
|
-
b !== null &&
|
|
37
|
-
typeof b === 'object' &&
|
|
38
|
-
typeof (b as any).Equals === 'function'
|
|
39
|
-
) {
|
|
40
|
-
return (b as any).Equals(a)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
26
|
if (a === b) return true
|
|
44
|
-
|
|
45
|
-
// Nullish equality (null == undefined)
|
|
46
|
-
if ((a === null || a === undefined) && (b === null || b === undefined))
|
|
27
|
+
if (typeof a === 'number' && typeof b === 'number' && isNaN(a) && isNaN(b)) {
|
|
47
28
|
return true
|
|
48
|
-
|
|
49
|
-
if (a === null || a === undefined || b === null || b === undefined)
|
|
50
|
-
return false
|
|
51
|
-
|
|
52
|
-
if (typeof a !== typeof b) return false
|
|
53
|
-
if (typeof a !== 'object') return false
|
|
54
|
-
|
|
55
|
-
// Arrays
|
|
56
|
-
if (Array.isArray(a) && Array.isArray(b)) {
|
|
57
|
-
if (a.length !== b.length) return false
|
|
58
|
-
return a.every((v, i) => isStructurallyEqual(v, b[i]))
|
|
59
29
|
}
|
|
60
|
-
if (
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const keysB = Object.keys(b as object)
|
|
65
|
-
if (keysA.length !== keysB.length) return false
|
|
66
|
-
return keysA.every((k) => isStructurallyEqual((a as any)[k], (b as any)[k]))
|
|
30
|
+
if ((a === null || a === undefined) && (b === null || b === undefined)) {
|
|
31
|
+
return true
|
|
32
|
+
}
|
|
33
|
+
return false
|
|
67
34
|
}
|
|
68
35
|
|
|
69
36
|
// --- Monadic Error Type ---
|
|
@@ -180,6 +147,18 @@ export interface RuntimeContext {
|
|
|
180
147
|
|
|
181
148
|
export type AtomExec = (step: any, ctx: RuntimeContext) => Promise<void>
|
|
182
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Effect classification of an atom.
|
|
152
|
+
* - `'pure'`: deterministic, no IO, no capability access, no observable side
|
|
153
|
+
* effects — safe inside a synchronous predicate (see the predicate verifier).
|
|
154
|
+
* - `'io'`: touches `ctx.capabilities` (fetch/store/llm/agent/code), or is
|
|
155
|
+
* nondeterministic (random/uuid), or has side effects (console). Not allowed
|
|
156
|
+
* in a predicate.
|
|
157
|
+
* Defaults to `'pure'`; effectful atoms must opt into `'io'`. The invariant
|
|
158
|
+
* "anything touching ctx.capabilities is tagged 'io'" is guarded by a test.
|
|
159
|
+
*/
|
|
160
|
+
export type AtomEffects = 'pure' | 'io'
|
|
161
|
+
|
|
183
162
|
export interface AtomDef {
|
|
184
163
|
op: OpCode
|
|
185
164
|
inputSchema: any
|
|
@@ -188,6 +167,7 @@ export interface AtomDef {
|
|
|
188
167
|
docs?: string
|
|
189
168
|
timeoutMs?: number
|
|
190
169
|
cost?: number | ((input: any, ctx: RuntimeContext) => number)
|
|
170
|
+
effects?: AtomEffects
|
|
191
171
|
}
|
|
192
172
|
|
|
193
173
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -199,6 +179,8 @@ export interface AtomOptions {
|
|
|
199
179
|
docs?: string
|
|
200
180
|
timeoutMs?: number
|
|
201
181
|
cost?: number | ((input: any, ctx: RuntimeContext) => number)
|
|
182
|
+
/** Effect class — defaults to `'pure'`; effectful atoms set `'io'`. */
|
|
183
|
+
effects?: AtomEffects
|
|
202
184
|
}
|
|
203
185
|
|
|
204
186
|
export interface RunResult {
|
|
@@ -1189,9 +1171,9 @@ export function evaluateExpr(node: ExprNode, ctx: RuntimeContext): any {
|
|
|
1189
1171
|
case '<=':
|
|
1190
1172
|
return left <= right
|
|
1191
1173
|
case '==':
|
|
1192
|
-
return
|
|
1174
|
+
return eqValue(left, right)
|
|
1193
1175
|
case '!=':
|
|
1194
|
-
return !
|
|
1176
|
+
return !eqValue(left, right)
|
|
1195
1177
|
case '===':
|
|
1196
1178
|
return left === right
|
|
1197
1179
|
case '!==':
|
|
@@ -1347,6 +1329,7 @@ export function defineAtom<I extends Record<string, any>, O = any>(
|
|
|
1347
1329
|
docs = '',
|
|
1348
1330
|
timeoutMs = 1000,
|
|
1349
1331
|
cost = 1,
|
|
1332
|
+
effects = 'pure',
|
|
1350
1333
|
} = typeof options === 'string' ? { docs: options } : options
|
|
1351
1334
|
|
|
1352
1335
|
const exec: AtomExec = async (step: any, ctx: RuntimeContext) => {
|
|
@@ -1446,6 +1429,7 @@ export function defineAtom<I extends Record<string, any>, O = any>(
|
|
|
1446
1429
|
docs,
|
|
1447
1430
|
timeoutMs,
|
|
1448
1431
|
cost,
|
|
1432
|
+
effects,
|
|
1449
1433
|
create: (input: I) => ({ op, ...input }),
|
|
1450
1434
|
}
|
|
1451
1435
|
}
|
|
@@ -3114,3 +3098,37 @@ export const coreAtoms = {
|
|
|
3114
3098
|
releaseProcedure,
|
|
3115
3099
|
clearExpiredProcedures,
|
|
3116
3100
|
}
|
|
3101
|
+
|
|
3102
|
+
/**
|
|
3103
|
+
* Effectful core atoms — anything that touches `ctx.capabilities`
|
|
3104
|
+
* (fetch/store/llm/agent/code), is nondeterministic (random/uuid), or has
|
|
3105
|
+
* observable side effects (console). Tagged centrally so the list reads as one
|
|
3106
|
+
* audit surface; a test asserts every capability-touching atom is in here.
|
|
3107
|
+
* Everything else defaults to `effects: 'pure'`.
|
|
3108
|
+
*/
|
|
3109
|
+
export const EFFECTFUL_CORE_OPS = [
|
|
3110
|
+
'httpFetch',
|
|
3111
|
+
'storeGet',
|
|
3112
|
+
'storeSet',
|
|
3113
|
+
'storeQuery',
|
|
3114
|
+
'storeVectorSearch',
|
|
3115
|
+
'llmPredict',
|
|
3116
|
+
'agentRun',
|
|
3117
|
+
'transpileCode',
|
|
3118
|
+
'runCode',
|
|
3119
|
+
'random',
|
|
3120
|
+
'uuid',
|
|
3121
|
+
'consoleLog',
|
|
3122
|
+
'consoleWarn',
|
|
3123
|
+
'consoleError',
|
|
3124
|
+
'storeProcedure',
|
|
3125
|
+
'releaseProcedure',
|
|
3126
|
+
'clearExpiredProcedures',
|
|
3127
|
+
'cache',
|
|
3128
|
+
'memoize',
|
|
3129
|
+
] as const
|
|
3130
|
+
|
|
3131
|
+
for (const op of EFFECTFUL_CORE_OPS) {
|
|
3132
|
+
const atom = (coreAtoms as Record<string, AtomDef>)[op]
|
|
3133
|
+
if (atom) atom.effects = 'io'
|
|
3134
|
+
}
|