@rcrsr/rill 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +37 -21
  2. package/dist/ext/crypto/index.d.ts +3 -3
  3. package/dist/ext/crypto/index.js +61 -58
  4. package/dist/ext/exec/index.d.ts +3 -3
  5. package/dist/ext/exec/index.js +14 -8
  6. package/dist/ext/fetch/index.d.ts +3 -3
  7. package/dist/ext/fetch/index.js +16 -11
  8. package/dist/ext/fs/index.d.ts +3 -3
  9. package/dist/ext/fs/index.js +242 -239
  10. package/dist/ext/kv/index.d.ts +3 -3
  11. package/dist/ext/kv/index.js +197 -195
  12. package/dist/ext/kv/store.js +2 -1
  13. package/dist/ext-parse-bridge.d.ts +10 -0
  14. package/dist/ext-parse-bridge.js +10 -0
  15. package/dist/generated/introspection-data.d.ts +1 -1
  16. package/dist/generated/introspection-data.js +385 -296
  17. package/dist/generated/version-data.d.ts +1 -1
  18. package/dist/generated/version-data.js +2 -2
  19. package/dist/index.d.ts +15 -4
  20. package/dist/index.js +14 -5
  21. package/dist/parser/parser-types.js +12 -0
  22. package/dist/parser/parser-use.js +7 -1
  23. package/dist/runtime/core/callable.d.ts +20 -8
  24. package/dist/runtime/core/callable.js +63 -23
  25. package/dist/runtime/core/context.d.ts +0 -11
  26. package/dist/runtime/core/context.js +76 -75
  27. package/dist/runtime/core/eval/index.d.ts +2 -2
  28. package/dist/runtime/core/eval/index.js +11 -0
  29. package/dist/runtime/core/eval/mixins/closures.js +15 -15
  30. package/dist/runtime/core/eval/mixins/conversion.js +51 -110
  31. package/dist/runtime/core/eval/mixins/core.js +2 -2
  32. package/dist/runtime/core/eval/mixins/expressions.js +35 -27
  33. package/dist/runtime/core/eval/mixins/literals.js +3 -3
  34. package/dist/runtime/core/eval/mixins/types.js +44 -54
  35. package/dist/runtime/core/eval/mixins/variables.js +10 -8
  36. package/dist/runtime/core/field-descriptor.d.ts +3 -3
  37. package/dist/runtime/core/field-descriptor.js +2 -1
  38. package/dist/runtime/core/introspection.js +6 -6
  39. package/dist/runtime/core/markers.d.ts +12 -0
  40. package/dist/runtime/core/markers.js +7 -0
  41. package/dist/runtime/core/type-registrations.d.ts +136 -0
  42. package/dist/runtime/core/type-registrations.js +749 -0
  43. package/dist/runtime/core/type-structures.d.ts +128 -0
  44. package/dist/runtime/core/type-structures.js +12 -0
  45. package/dist/runtime/core/types.d.ts +15 -3
  46. package/dist/runtime/core/values.d.ts +62 -153
  47. package/dist/runtime/core/values.js +308 -524
  48. package/dist/runtime/ext/builtins.js +83 -64
  49. package/dist/runtime/ext/extensions.d.ts +30 -124
  50. package/dist/runtime/ext/extensions.js +0 -93
  51. package/dist/runtime/ext/test-context.d.ts +28 -0
  52. package/dist/runtime/ext/test-context.js +154 -0
  53. package/dist/runtime/index.d.ts +22 -8
  54. package/dist/runtime/index.js +18 -4
  55. package/dist/signature-parser.d.ts +2 -2
  56. package/dist/signature-parser.js +14 -14
  57. package/package.json +1 -1
@@ -9,8 +9,9 @@
9
9
  import { callable, isCallable, isDict } from '../core/callable.js';
10
10
  import { RuntimeError } from '../../types.js';
11
11
  import { parseSignatureRegistration } from '../../signature-parser.js';
12
- import { anyTypeValue, deepEquals, formatValue, inferType, isEmpty, isRillIterator, isVector, rillTypeToTypeValue, valueToJSON, } from '../core/values.js';
12
+ import { anyTypeValue, deepEquals, formatValue, inferType, isEmpty, isRillIterator, isVector, rillTypeToTypeValue, serializeValue, } from '../core/values.js';
13
13
  import { invokeCallable } from '../core/eval/index.js';
14
+ import { populateBuiltinMethods } from '../core/type-registrations.js';
14
15
  // ============================================================
15
16
  // ITERATOR HELPERS
16
17
  // ============================================================
@@ -70,7 +71,7 @@ export const BUILTIN_FUNCTIONS = {
70
71
  params: [
71
72
  {
72
73
  name: 'value',
73
- type: { type: 'any' },
74
+ type: { kind: 'any' },
74
75
  defaultValue: undefined,
75
76
  annotations: {},
76
77
  },
@@ -83,7 +84,7 @@ export const BUILTIN_FUNCTIONS = {
83
84
  params: [
84
85
  {
85
86
  name: 'message',
86
- type: { type: 'any' },
87
+ type: { kind: 'any' },
87
88
  defaultValue: undefined,
88
89
  annotations: {},
89
90
  },
@@ -103,16 +104,16 @@ export const BUILTIN_FUNCTIONS = {
103
104
  params: [
104
105
  {
105
106
  name: 'value',
106
- type: { type: 'any' },
107
+ type: { kind: 'any' },
107
108
  defaultValue: undefined,
108
109
  annotations: {},
109
110
  },
110
111
  ],
111
- returnType: rillTypeToTypeValue({ type: 'string' }),
112
+ returnType: rillTypeToTypeValue({ kind: 'string' }),
112
113
  fn: (args, _ctx, location) => {
113
114
  const value = args['value'] ?? null;
114
115
  try {
115
- const jsonValue = valueToJSON(value);
116
+ const jsonValue = serializeValue(value);
116
117
  return JSON.stringify(jsonValue);
117
118
  }
118
119
  catch (err) {
@@ -135,14 +136,14 @@ export const BUILTIN_FUNCTIONS = {
135
136
  {
136
137
  name: 'items',
137
138
  type: {
138
- type: 'union',
139
- members: [{ type: 'list' }, { type: 'dict' }, { type: 'string' }],
139
+ kind: 'union',
140
+ members: [{ kind: 'list' }, { kind: 'dict' }, { kind: 'string' }],
140
141
  },
141
142
  defaultValue: undefined,
142
143
  annotations: {},
143
144
  },
144
145
  ],
145
- returnType: rillTypeToTypeValue({ type: 'list' }),
146
+ returnType: rillTypeToTypeValue({ kind: 'list' }),
146
147
  fn: (args) => {
147
148
  const input = args['items'] ?? null;
148
149
  if (Array.isArray(input)) {
@@ -167,19 +168,19 @@ export const BUILTIN_FUNCTIONS = {
167
168
  params: [
168
169
  {
169
170
  name: 'start',
170
- type: { type: 'number' },
171
+ type: { kind: 'number' },
171
172
  defaultValue: undefined,
172
173
  annotations: {},
173
174
  },
174
175
  {
175
176
  name: 'stop',
176
- type: { type: 'number' },
177
+ type: { kind: 'number' },
177
178
  defaultValue: undefined,
178
179
  annotations: {},
179
180
  },
180
181
  {
181
182
  name: 'step',
182
- type: { type: 'number' },
183
+ type: { kind: 'number' },
183
184
  defaultValue: 1,
184
185
  annotations: {},
185
186
  },
@@ -217,13 +218,13 @@ export const BUILTIN_FUNCTIONS = {
217
218
  params: [
218
219
  {
219
220
  name: 'value',
220
- type: { type: 'any' },
221
+ type: { kind: 'any' },
221
222
  defaultValue: undefined,
222
223
  annotations: {},
223
224
  },
224
225
  {
225
226
  name: 'count',
226
- type: { type: 'number' },
227
+ type: { kind: 'number' },
227
228
  defaultValue: undefined,
228
229
  annotations: {},
229
230
  },
@@ -262,13 +263,13 @@ export const BUILTIN_FUNCTIONS = {
262
263
  params: [
263
264
  {
264
265
  name: 'value',
265
- type: { type: 'any' },
266
+ type: { kind: 'any' },
266
267
  defaultValue: undefined,
267
268
  annotations: {},
268
269
  },
269
270
  {
270
271
  name: 'transform',
271
- type: { type: 'any' },
272
+ type: { kind: 'any' },
272
273
  defaultValue: undefined,
273
274
  annotations: {},
274
275
  },
@@ -311,20 +312,10 @@ export const BUILTIN_FUNCTIONS = {
311
312
  // ============================================================
312
313
  // BUILT-IN METHODS
313
314
  // ============================================================
314
- /** Factory for comparison methods (lt, gt, le, ge) */
315
- function createComparisonMethod(compare) {
316
- return (receiver, args) => {
317
- const arg = args[0];
318
- if (typeof receiver === 'number' && typeof arg === 'number') {
319
- return compare(receiver, arg);
320
- }
321
- return compare(formatValue(receiver), formatValue(arg ?? ''));
322
- };
323
- }
324
315
  /** Receiver param prepended to every method's param list */
325
316
  const RECEIVER_PARAM = {
326
317
  name: 'receiver',
327
- type: { type: 'any' },
318
+ type: { kind: 'any' },
328
319
  defaultValue: undefined,
329
320
  annotations: {},
330
321
  };
@@ -337,7 +328,7 @@ const RECEIVER_PARAM = {
337
328
  *
338
329
  * EC-4: Receiver missing from record raises RILL-R044.
339
330
  */
340
- function buildMethodEntry(name, signature, method) {
331
+ function buildMethodEntry(name, signature, method, skipReceiverValidation) {
341
332
  const parsed = parseSignatureRegistration(signature, name);
342
333
  const methodParams = parsed.params;
343
334
  return {
@@ -361,6 +352,7 @@ function buildMethodEntry(name, signature, method) {
361
352
  returnType: parsed.returnType !== undefined
362
353
  ? rillTypeToTypeValue(parsed.returnType)
363
354
  : anyTypeValue,
355
+ ...(skipReceiverValidation ? { skipReceiverValidation: true } : {}),
364
356
  };
365
357
  }
366
358
  export const BUILTIN_METHODS = {
@@ -552,10 +544,34 @@ const mPadEnd = (receiver, args) => {
552
544
  const mEq = (receiver, args) => deepEquals(receiver, args[0] ?? null);
553
545
  /** Inequality check (deep structural comparison) */
554
546
  const mNe = (receiver, args) => !deepEquals(receiver, args[0] ?? null);
555
- const mLt = createComparisonMethod((a, b) => a < b);
556
- const mGt = createComparisonMethod((a, b) => a > b);
557
- const mLe = createComparisonMethod((a, b) => a <= b);
558
- const mGe = createComparisonMethod((a, b) => a >= b);
547
+ /** Less-than comparison (number or string) */
548
+ const mLt = (receiver, args) => {
549
+ const arg = args[0];
550
+ if (typeof receiver === 'number' && typeof arg === 'number')
551
+ return receiver < arg;
552
+ return formatValue(receiver) < formatValue(arg ?? '');
553
+ };
554
+ /** Greater-than comparison (number or string) */
555
+ const mGt = (receiver, args) => {
556
+ const arg = args[0];
557
+ if (typeof receiver === 'number' && typeof arg === 'number')
558
+ return receiver > arg;
559
+ return formatValue(receiver) > formatValue(arg ?? '');
560
+ };
561
+ /** Less-than-or-equal comparison (number or string) */
562
+ const mLe = (receiver, args) => {
563
+ const arg = args[0];
564
+ if (typeof receiver === 'number' && typeof arg === 'number')
565
+ return receiver <= arg;
566
+ return formatValue(receiver) <= formatValue(arg ?? '');
567
+ };
568
+ /** Greater-than-or-equal comparison (number or string) */
569
+ const mGe = (receiver, args) => {
570
+ const arg = args[0];
571
+ if (typeof receiver === 'number' && typeof arg === 'number')
572
+ return receiver >= arg;
573
+ return formatValue(receiver) >= formatValue(arg ?? '');
574
+ };
559
575
  /** Get all keys of a dict as a list */
560
576
  const mKeys = (receiver) => isDict(receiver) ? Object.keys(receiver) : [];
561
577
  /** Get all values of a dict as a list */
@@ -757,10 +773,10 @@ const SIG_CMP = '|other: any|:bool';
757
773
  BUILTIN_METHODS.string = Object.freeze({
758
774
  len: buildMethodEntry('len', SIG_LEN, mLen),
759
775
  trim: buildMethodEntry('trim', '||:string', mTrim),
760
- head: buildMethodEntry('head', SIG_HEAD, mHead),
761
- tail: buildMethodEntry('tail', SIG_TAIL, mTail),
762
- first: buildMethodEntry('first', SIG_FIRST, mFirst),
763
- at: buildMethodEntry('at', SIG_AT, mAt),
776
+ head: buildMethodEntry('head', SIG_HEAD, mHead, true),
777
+ tail: buildMethodEntry('tail', SIG_TAIL, mTail, true),
778
+ first: buildMethodEntry('first', SIG_FIRST, mFirst, true),
779
+ at: buildMethodEntry('at', SIG_AT, mAt, true),
764
780
  split: buildMethodEntry('split', '|separator: string = "\\n"|:list', mSplit),
765
781
  lines: buildMethodEntry('lines', '||:list', mLines),
766
782
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
@@ -777,8 +793,8 @@ BUILTIN_METHODS.string = Object.freeze({
777
793
  repeat: buildMethodEntry('repeat', '|count: number|:string', mRepeat),
778
794
  pad_start: buildMethodEntry('pad_start', '|length: number, fill: string = " "|:string', mPadStart),
779
795
  pad_end: buildMethodEntry('pad_end', '|length: number, fill: string = " "|:string', mPadEnd),
780
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
781
- ne: buildMethodEntry('ne', SIG_NE, mNe),
796
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
797
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
782
798
  lt: buildMethodEntry('lt', SIG_CMP, mLt),
783
799
  gt: buildMethodEntry('gt', SIG_CMP, mGt),
784
800
  le: buildMethodEntry('le', SIG_CMP, mLe),
@@ -786,32 +802,32 @@ BUILTIN_METHODS.string = Object.freeze({
786
802
  });
787
803
  BUILTIN_METHODS.list = Object.freeze({
788
804
  len: buildMethodEntry('len', SIG_LEN, mLen),
789
- head: buildMethodEntry('head', SIG_HEAD, mHead),
790
- tail: buildMethodEntry('tail', SIG_TAIL, mTail),
791
- first: buildMethodEntry('first', SIG_FIRST, mFirst),
792
- at: buildMethodEntry('at', SIG_AT, mAt),
805
+ head: buildMethodEntry('head', SIG_HEAD, mHead, true),
806
+ tail: buildMethodEntry('tail', SIG_TAIL, mTail, true),
807
+ first: buildMethodEntry('first', SIG_FIRST, mFirst, true),
808
+ at: buildMethodEntry('at', SIG_AT, mAt, true),
793
809
  join: buildMethodEntry('join', '|separator: string = ","|:string', mJoin),
794
810
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
795
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
796
- ne: buildMethodEntry('ne', SIG_NE, mNe),
797
- has: buildMethodEntry('has', '|value: any|:bool', mHas),
798
- has_any: buildMethodEntry('has_any', '|candidates: list|:bool', mHasAny),
799
- has_all: buildMethodEntry('has_all', '|candidates: list|:bool', mHasAll),
811
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
812
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
813
+ has: buildMethodEntry('has', '|value: any|:bool', mHas, true),
814
+ has_any: buildMethodEntry('has_any', '|candidates: list|:bool', mHasAny, true),
815
+ has_all: buildMethodEntry('has_all', '|candidates: list|:bool', mHasAll, true),
800
816
  });
801
817
  BUILTIN_METHODS.dict = Object.freeze({
802
818
  len: buildMethodEntry('len', SIG_LEN, mLen),
803
- first: buildMethodEntry('first', SIG_FIRST, mFirst),
819
+ first: buildMethodEntry('first', SIG_FIRST, mFirst, true),
804
820
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
805
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
806
- ne: buildMethodEntry('ne', SIG_NE, mNe),
807
- keys: buildMethodEntry('keys', '||:list', mKeys),
808
- values: buildMethodEntry('values', '||:list', mValues),
809
- entries: buildMethodEntry('entries', '||:list', mEntries),
821
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
822
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
823
+ keys: buildMethodEntry('keys', '||:list', mKeys, true),
824
+ values: buildMethodEntry('values', '||:list', mValues, true),
825
+ entries: buildMethodEntry('entries', '||:list', mEntries, true),
810
826
  });
811
827
  BUILTIN_METHODS.number = Object.freeze({
812
828
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
813
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
814
- ne: buildMethodEntry('ne', SIG_NE, mNe),
829
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
830
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
815
831
  lt: buildMethodEntry('lt', SIG_CMP, mLt),
816
832
  gt: buildMethodEntry('gt', SIG_CMP, mGt),
817
833
  le: buildMethodEntry('le', SIG_CMP, mLe),
@@ -819,18 +835,21 @@ BUILTIN_METHODS.number = Object.freeze({
819
835
  });
820
836
  BUILTIN_METHODS.bool = Object.freeze({
821
837
  empty: buildMethodEntry('empty', SIG_EMPTY, mEmpty),
822
- eq: buildMethodEntry('eq', SIG_EQ, mEq),
823
- ne: buildMethodEntry('ne', SIG_NE, mNe),
838
+ eq: buildMethodEntry('eq', SIG_EQ, mEq, true),
839
+ ne: buildMethodEntry('ne', SIG_NE, mNe, true),
824
840
  });
825
841
  // [ASSUMPTION] vector is a 6th group beyond the 5 specified basic types.
826
842
  // The 7 vector methods do not belong to string/list/dict/number/bool.
827
843
  // Adding this group ensures all 42 methods are accessible (AC-36).
828
844
  BUILTIN_METHODS.vector = Object.freeze({
829
- dimensions: buildMethodEntry('dimensions', '||:number', mDimensions),
830
- model: buildMethodEntry('model', '||:string', mModel),
831
- similarity: buildMethodEntry('similarity', '|other: any|:number', mSimilarity),
832
- dot: buildMethodEntry('dot', '|other: any|:number', mDot),
833
- distance: buildMethodEntry('distance', '|other: any|:number', mDistance),
834
- norm: buildMethodEntry('norm', '||:number', mNorm),
835
- normalize: buildMethodEntry('normalize', '||:any', mNormalize),
845
+ dimensions: buildMethodEntry('dimensions', '||:number', mDimensions, true),
846
+ model: buildMethodEntry('model', '||:string', mModel, true),
847
+ similarity: buildMethodEntry('similarity', '|other: any|:number', mSimilarity, true),
848
+ dot: buildMethodEntry('dot', '|other: any|:number', mDot, true),
849
+ distance: buildMethodEntry('distance', '|other: any|:number', mDistance, true),
850
+ norm: buildMethodEntry('norm', '||:number', mNorm, true),
851
+ normalize: buildMethodEntry('normalize', '||:any', mNormalize, true),
836
852
  });
853
+ // Populate registration methods from BUILTIN_METHODS at module load time.
854
+ // No circular dependency: type-registrations.ts does not import builtins.ts.
855
+ populateBuiltinMethods(BUILTIN_METHODS);
@@ -1,5 +1,6 @@
1
- import type { RillFunction } from '../core/callable.js';
1
+ import type { ApplicationCallable } from '../core/callable.js';
2
2
  import type { ExtensionEvent, RuntimeCallbacks } from '../core/types.js';
3
+ import type { RillValue } from '../core/values.js';
3
4
  /**
4
5
  * Minimal interface for extension event emission.
5
6
  * Allows emitExtensionEvent to accept any context with callbacks.
@@ -9,27 +10,20 @@ interface RuntimeContextLike {
9
10
  }
10
11
  /**
11
12
  * Result object returned by extension factories.
12
- * Contains host function definitions with optional cleanup and session lifecycle hooks.
13
+ * Contains the mounted RillValue with optional lifecycle hooks.
14
+ * Lifecycle hooks live on the factory result, not on the value dict (DD-1).
13
15
  */
14
- export interface ExtensionResult {
15
- [name: string]: RillFunction | ((...args: never[]) => unknown) | undefined;
16
+ export interface ExtensionFactoryResult {
17
+ readonly value: RillValue;
16
18
  dispose?: () => void | Promise<void>;
17
19
  suspend?: () => unknown;
18
20
  restore?: (state: unknown) => void;
19
21
  }
20
- /**
21
- * Result object returned by hoistExtension.
22
- * Separates functions from dispose for safe createRuntimeContext usage.
23
- */
24
- export interface HoistedExtension {
25
- functions: Record<string, RillFunction>;
26
- dispose?: () => void | Promise<void>;
27
- }
28
22
  /**
29
23
  * Factory function contract for creating extensions.
30
24
  * Accepts typed configuration and returns isolated instance.
31
25
  */
32
- export type ExtensionFactory<TConfig> = (config: TConfig) => ExtensionResult | Promise<ExtensionResult>;
26
+ export type ExtensionFactory<TConfig> = (config: TConfig) => ExtensionFactoryResult | Promise<ExtensionFactoryResult>;
33
27
  /**
34
28
  * Descriptor for a single configuration field in an extension schema.
35
29
  * The secret flag is advisory: harness tooling uses it to mask or omit values.
@@ -72,18 +66,17 @@ export interface ExtensionManifest {
72
66
  * - mounts(): List all configured mounts
73
67
  */
74
68
  export type KvExtensionContract = {
75
- readonly get: RillFunction;
76
- readonly get_or: RillFunction;
77
- readonly set: RillFunction;
78
- readonly merge: RillFunction;
79
- readonly delete: RillFunction;
80
- readonly keys: RillFunction;
81
- readonly has: RillFunction;
82
- readonly clear: RillFunction;
83
- readonly getAll: RillFunction;
84
- readonly schema: RillFunction;
85
- readonly mounts: RillFunction;
86
- readonly dispose?: (() => void | Promise<void>) | undefined;
69
+ readonly get: ApplicationCallable;
70
+ readonly get_or: ApplicationCallable;
71
+ readonly set: ApplicationCallable;
72
+ readonly merge: ApplicationCallable;
73
+ readonly delete: ApplicationCallable;
74
+ readonly keys: ApplicationCallable;
75
+ readonly has: ApplicationCallable;
76
+ readonly clear: ApplicationCallable;
77
+ readonly getAll: ApplicationCallable;
78
+ readonly schema: ApplicationCallable;
79
+ readonly mounts: ApplicationCallable;
87
80
  };
88
81
  /**
89
82
  * Contract type for fs extension implementations.
@@ -104,106 +97,19 @@ export type KvExtensionContract = {
104
97
  * - mounts(): List all configured mounts
105
98
  */
106
99
  export type FsExtensionContract = {
107
- readonly read: RillFunction;
108
- readonly write: RillFunction;
109
- readonly append: RillFunction;
110
- readonly list: RillFunction;
111
- readonly find: RillFunction;
112
- readonly exists: RillFunction;
113
- readonly remove: RillFunction;
114
- readonly stat: RillFunction;
115
- readonly mkdir: RillFunction;
116
- readonly copy: RillFunction;
117
- readonly move: RillFunction;
118
- readonly mounts: RillFunction;
119
- readonly dispose?: (() => void | Promise<void>) | undefined;
120
- };
121
- /**
122
- * Contract type for llm extension implementations.
123
- * Enforces exact function structure for compile-time verification.
124
- *
125
- * Backend implementations must provide all 6 functions:
126
- * - message(text, options): Send single message
127
- * - messages(messages, options): Multi-turn conversation
128
- * - embed(text): Generate embedding vector
129
- * - embed_batch(texts): Batch embeddings
130
- * - tool_loop(prompt, options): Tool use orchestration
131
- * - generate(prompt, options): Structured output extraction
132
- */
133
- export type LlmExtensionContract = {
134
- readonly message: RillFunction;
135
- readonly messages: RillFunction;
136
- readonly embed: RillFunction;
137
- readonly embed_batch: RillFunction;
138
- readonly tool_loop: RillFunction;
139
- readonly generate: RillFunction;
140
- readonly dispose?: (() => void | Promise<void>) | undefined;
141
- };
142
- /**
143
- * Contract type for vector extension implementations.
144
- * Enforces exact function structure for compile-time verification.
145
- *
146
- * Backend implementations must provide all 11 functions:
147
- * - upsert(id, vector, metadata): Insert or update vector
148
- * - upsert_batch(items): Batch insert/update
149
- * - search(vector, options): Search k nearest neighbors
150
- * - get(id): Fetch vector by ID
151
- * - delete(id): Delete vector by ID
152
- * - delete_batch(ids): Batch delete
153
- * - count(): Count vectors in collection
154
- * - create_collection(name, options): Create collection
155
- * - delete_collection(name): Delete collection
156
- * - list_collections(): List all collections
157
- * - describe(): Get collection metadata
158
- */
159
- export type VectorExtensionContract = {
160
- readonly upsert: RillFunction;
161
- readonly upsert_batch: RillFunction;
162
- readonly search: RillFunction;
163
- readonly get: RillFunction;
164
- readonly delete: RillFunction;
165
- readonly delete_batch: RillFunction;
166
- readonly count: RillFunction;
167
- readonly create_collection: RillFunction;
168
- readonly delete_collection: RillFunction;
169
- readonly list_collections: RillFunction;
170
- readonly describe: RillFunction;
171
- readonly dispose?: (() => void | Promise<void>) | undefined;
100
+ readonly read: ApplicationCallable;
101
+ readonly write: ApplicationCallable;
102
+ readonly append: ApplicationCallable;
103
+ readonly list: ApplicationCallable;
104
+ readonly find: ApplicationCallable;
105
+ readonly exists: ApplicationCallable;
106
+ readonly remove: ApplicationCallable;
107
+ readonly stat: ApplicationCallable;
108
+ readonly mkdir: ApplicationCallable;
109
+ readonly copy: ApplicationCallable;
110
+ readonly move: ApplicationCallable;
111
+ readonly mounts: ApplicationCallable;
172
112
  };
173
- /**
174
- * Prefix all function names in an extension with a namespace.
175
- *
176
- * @param namespace - Alphanumeric string with underscores/hyphens (e.g., "fs", "claude_code")
177
- * @param functions - Extension result with function definitions
178
- * @returns New ExtensionResult with prefixed function names (namespace::functionName)
179
- * @throws {RuntimeError} RUNTIME_TYPE_ERROR if namespace is invalid
180
- *
181
- * @example
182
- * ```typescript
183
- * const fs = createFsExtension();
184
- * const prefixed = prefixFunctions("fs", fs);
185
- * // { "fs::read": ..., "fs::write": ..., dispose: ... }
186
- * ```
187
- */
188
- export declare function prefixFunctions(namespace: string, functions: ExtensionResult): ExtensionResult;
189
- /**
190
- * Separate dispose from functions for safe createRuntimeContext usage.
191
- * Wraps prefixFunctions and returns separated structure.
192
- *
193
- * @param namespace - String matching /^[a-zA-Z0-9_-]+$/
194
- * @param extension - Output from extension factory
195
- * @returns Separated functions and dispose handler
196
- * @throws {Error} If namespace is empty
197
- * @throws {Error} If namespace has invalid format
198
- * @throws {TypeError} If extension is null or undefined
199
- *
200
- * @example
201
- * ```typescript
202
- * const { functions, dispose } = hoistExtension('db', dbExtension);
203
- * const ctx = createRuntimeContext({ functions });
204
- * ```
205
- */
206
- export declare function hoistExtension(namespace: string, extension: ExtensionResult): HoistedExtension;
207
113
  /**
208
114
  * Emit an extension event with auto-generated timestamp.
209
115
  * Adds ISO timestamp if event.timestamp is undefined, then calls onLogEvent callback.
@@ -1,96 +1,3 @@
1
- /**
2
- * Prefix all function names in an extension with a namespace.
3
- *
4
- * @param namespace - Alphanumeric string with underscores/hyphens (e.g., "fs", "claude_code")
5
- * @param functions - Extension result with function definitions
6
- * @returns New ExtensionResult with prefixed function names (namespace::functionName)
7
- * @throws {RuntimeError} RUNTIME_TYPE_ERROR if namespace is invalid
8
- *
9
- * @example
10
- * ```typescript
11
- * const fs = createFsExtension();
12
- * const prefixed = prefixFunctions("fs", fs);
13
- * // { "fs::read": ..., "fs::write": ..., dispose: ... }
14
- * ```
15
- */
16
- export function prefixFunctions(namespace, functions) {
17
- // EC-7: Extension not object
18
- if (typeof functions !== 'object' ||
19
- functions === null ||
20
- Array.isArray(functions)) {
21
- throw new TypeError('Extension must be an object');
22
- }
23
- // EC-6: Invalid namespace format
24
- const NAMESPACE_PATTERN = /^[a-zA-Z0-9_-]+$/;
25
- if (!NAMESPACE_PATTERN.test(namespace)) {
26
- throw new Error('Invalid namespace format: must match /^[a-zA-Z0-9_-]+$/');
27
- }
28
- // Create new object with prefixed keys
29
- const result = {};
30
- for (const [name, definition] of Object.entries(functions)) {
31
- // Skip lifecycle hooks during prefixing
32
- if (name === 'dispose' || name === 'suspend' || name === 'restore')
33
- continue;
34
- result[`${namespace}::${name}`] = definition;
35
- }
36
- // Preserve lifecycle hooks if present
37
- const resultWithHooks = result;
38
- if (functions.dispose !== undefined) {
39
- resultWithHooks.dispose = functions.dispose;
40
- }
41
- if (functions.suspend !== undefined) {
42
- resultWithHooks.suspend = functions.suspend;
43
- }
44
- if (functions.restore !== undefined) {
45
- resultWithHooks.restore = functions.restore;
46
- }
47
- return resultWithHooks;
48
- }
49
- /**
50
- * Separate dispose from functions for safe createRuntimeContext usage.
51
- * Wraps prefixFunctions and returns separated structure.
52
- *
53
- * @param namespace - String matching /^[a-zA-Z0-9_-]+$/
54
- * @param extension - Output from extension factory
55
- * @returns Separated functions and dispose handler
56
- * @throws {Error} If namespace is empty
57
- * @throws {Error} If namespace has invalid format
58
- * @throws {TypeError} If extension is null or undefined
59
- *
60
- * @example
61
- * ```typescript
62
- * const { functions, dispose } = hoistExtension('db', dbExtension);
63
- * const ctx = createRuntimeContext({ functions });
64
- * ```
65
- */
66
- export function hoistExtension(namespace, extension) {
67
- // EC-3: Null/undefined extension
68
- if (extension === null || extension === undefined) {
69
- throw new TypeError('Extension cannot be null or undefined');
70
- }
71
- // EC-2: Empty namespace
72
- if (namespace === '') {
73
- throw new Error('Namespace cannot be empty');
74
- }
75
- // EC-1: Invalid namespace format
76
- const NAMESPACE_PATTERN = /^[a-zA-Z0-9_-]+$/;
77
- if (!NAMESPACE_PATTERN.test(namespace)) {
78
- throw new Error('Invalid namespace format: must match /^[a-zA-Z0-9_-]+$/');
79
- }
80
- // Call prefixFunctions internally
81
- const prefixed = prefixFunctions(namespace, extension);
82
- // Extract dispose from result
83
- const { dispose, ...functions } = prefixed;
84
- // Return separated structure
85
- const result = {
86
- functions: functions,
87
- };
88
- // Only add dispose if it exists (exactOptionalPropertyTypes)
89
- if (dispose !== undefined) {
90
- result.dispose = dispose;
91
- }
92
- return result;
93
- }
94
1
  /**
95
2
  * Emit an extension event with auto-generated timestamp.
96
3
  * Adds ISO timestamp if event.timestamp is undefined, then calls onLogEvent callback.
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Test Context Factory
3
+ *
4
+ * Creates a pre-wired RuntimeContext from extension value maps.
5
+ * Designed for testing and lightweight embedding without rill-config.
6
+ */
7
+ import type { RuntimeContext } from '../core/types.js';
8
+ import { type RillValue } from '../core/values.js';
9
+ /**
10
+ * Error thrown when extension binding generation fails.
11
+ * Mirrors the ExtensionBindingError in rill-config for core-only usage.
12
+ */
13
+ export declare class ExtensionBindingError extends Error {
14
+ readonly code: "EXTENSION_BINDING";
15
+ constructor(message: string);
16
+ }
17
+ /**
18
+ * Create a RuntimeContext pre-wired with extension values.
19
+ * Builds extension bindings, registers ext and module resolvers,
20
+ * and returns a context ready for execute().
21
+ *
22
+ * @throws {TypeError} when an extension value is undefined (EC-9)
23
+ * @throws {ExtensionBindingError} when binding generation fails (EC-10)
24
+ */
25
+ export declare function createTestContext(extensions: Record<string, {
26
+ value: RillValue;
27
+ dispose?: () => void | Promise<void>;
28
+ }>): RuntimeContext;