capdag 0.126.278 → 0.127.280

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 (3) hide show
  1. package/capdag.js +196 -49
  2. package/capdag.test.js +42 -42
  3. package/package.json +1 -1
package/capdag.js CHANGED
@@ -789,18 +789,22 @@ const MEDIA_OBJECT = 'media:record';
789
789
  // Media URN for binary data - the most general media type (no constraints)
790
790
  const MEDIA_IDENTITY = 'media:';
791
791
 
792
- // Array types - URNs must match base.toml definitions
793
- // Media URN for string array type - textable with list marker
794
- const MEDIA_STRING_ARRAY = 'media:list;textable';
795
- // Media URN for integer array type - textable, numeric with list marker
796
- const MEDIA_INTEGER_ARRAY = 'media:integer;list;textable;numeric';
797
- // Media URN for number array type - textable, numeric with list marker
798
- const MEDIA_NUMBER_ARRAY = 'media:list;textable;numeric';
799
- // Media URN for boolean array type - uses "bool" with list marker
800
- const MEDIA_BOOLEAN_ARRAY = 'media:bool;list;textable';
801
- // Media URN for object array type - list of records (NOT textable)
802
- // Use a specific format like JSON array for textable object arrays.
803
- const MEDIA_OBJECT_ARRAY = 'media:list;record';
792
+ // List types - URNs must match base.toml definitions
793
+ // Media URN for generic list type
794
+ const MEDIA_LIST = 'media:list';
795
+ // Media URN for textable list type
796
+ const MEDIA_TEXTABLE_LIST = 'media:list;textable';
797
+ // Media URN for string list type - textable with list marker
798
+ const MEDIA_STRING_LIST = 'media:list;textable';
799
+ // Media URN for integer list type - textable, numeric with list marker
800
+ const MEDIA_INTEGER_LIST = 'media:integer;list;textable;numeric';
801
+ // Media URN for number list type - textable, numeric with list marker
802
+ const MEDIA_NUMBER_LIST = 'media:list;numeric;textable';
803
+ // Media URN for boolean list type - uses "bool" with list marker
804
+ const MEDIA_BOOLEAN_LIST = 'media:bool;list;textable';
805
+ // Media URN for object list type - list of records (NOT textable)
806
+ // Use a specific format like JSON array for textable object lists.
807
+ const MEDIA_OBJECT_LIST = 'media:list;record';
804
808
 
805
809
  // Semantic media types for specialized content
806
810
  // Media URN for PNG image data
@@ -813,8 +817,6 @@ const MEDIA_VIDEO = 'media:video';
813
817
  // Semantic AI input types - distinguished by their purpose/context
814
818
  // Media URN for audio input containing speech for transcription (Whisper)
815
819
  const MEDIA_AUDIO_SPEECH = 'media:audio;wav;speech';
816
- // Media URN for thumbnail image output
817
- const MEDIA_IMAGE_THUMBNAIL = 'media:image;png;thumbnail';
818
820
 
819
821
  // Document types (PRIMARY naming - type IS the format)
820
822
  // Media URN for PDF documents
@@ -842,6 +844,18 @@ const MEDIA_JSON_SCHEMA = 'media:json;json-schema;record;textable';
842
844
  // Media URN for YAML data - has record marker (structured key-value)
843
845
  const MEDIA_YAML = 'media:record;textable;yaml';
844
846
 
847
+ // Format-specific variants for JSON, YAML, CSV
848
+ const MEDIA_JSON_VALUE = 'media:json;textable';
849
+ const MEDIA_JSON_RECORD = 'media:json;record;textable';
850
+ const MEDIA_JSON_LIST = 'media:json;list;textable';
851
+ const MEDIA_JSON_LIST_RECORD = 'media:json;list;record;textable';
852
+ const MEDIA_YAML_VALUE = 'media:textable;yaml';
853
+ const MEDIA_YAML_RECORD = 'media:record;textable;yaml';
854
+ const MEDIA_YAML_LIST = 'media:list;textable;yaml';
855
+ const MEDIA_YAML_LIST_RECORD = 'media:list;record;textable;yaml';
856
+ const MEDIA_CSV = 'media:csv;list;record;textable';
857
+ const MEDIA_CSV_LIST = 'media:csv;list;textable';
858
+
845
859
  // File path types - for arguments that represent filesystem paths
846
860
  // Media URN for a single file path - textable, scalar by default (no list marker)
847
861
  const MEDIA_FILE_PATH = 'media:file-path;textable';
@@ -849,8 +863,6 @@ const MEDIA_FILE_PATH = 'media:file-path;textable';
849
863
  const MEDIA_FILE_PATH_ARRAY = 'media:file-path;list;textable';
850
864
 
851
865
  // Semantic text input types - distinguished by their purpose/context
852
- // Media URN for frontmatter text (book metadata) - scalar by default
853
- const MEDIA_FRONTMATTER_TEXT = 'media:frontmatter;textable';
854
866
  // Media URN for model spec (provider:model format, HuggingFace name, etc.) - scalar by default
855
867
  const MEDIA_MODEL_SPEC = 'media:model-spec;textable';
856
868
  // Media URN for MLX model path - scalar by default
@@ -877,20 +889,17 @@ const MEDIA_PATH_OUTPUT = 'media:model-path;record;textable';
877
889
  const MEDIA_EMBEDDING_VECTOR = 'media:embedding-vector;record;textable';
878
890
  // Media URN for LLM inference output - has record marker
879
891
  const MEDIA_LLM_INFERENCE_OUTPUT = 'media:generated-text;record;textable';
880
- // Media URN for extracted metadata - has record marker
881
- const MEDIA_FILE_METADATA = 'media:file-metadata;record;textable';
882
- // Media URN for extracted outline - has record marker
883
- const MEDIA_DOCUMENT_OUTLINE = 'media:document-outline;record;textable';
884
- // Media URN for disbound page - has list marker (array of page objects)
885
- const MEDIA_DISBOUND_PAGE = 'media:disbound-page;list;textable';
886
892
  // Media URN for vision inference output - textable, scalar by default
887
893
  const MEDIA_IMAGE_DESCRIPTION = 'media:image-description;textable';
888
894
  // Media URN for transcription output - has record marker
889
895
  const MEDIA_TRANSCRIPTION_OUTPUT = 'media:record;textable;transcription';
890
- // Media URN for decision output (bit choice) - scalar by default
891
- const MEDIA_DECISION = 'media:bool;decision;textable';
892
- // Media URN for decision array output (bit choices) - has list marker
893
- const MEDIA_DECISION_ARRAY = 'media:bool;decision;list;textable';
896
+ // Media URN for decision output - JSON record with textable
897
+ const MEDIA_DECISION = 'media:decision;json;record;textable';
898
+ // Media URN for textable page output
899
+ const MEDIA_TEXTABLE_PAGE = 'media:textable;page';
900
+ // Collection types
901
+ const MEDIA_COLLECTION = 'media:collection;record';
902
+ const MEDIA_COLLECTION_LIST = 'media:collection;list;record';
894
903
 
895
904
  // =============================================================================
896
905
  // STANDARD CAP URN CONSTANTS
@@ -956,8 +965,10 @@ class MediaUrn {
956
965
  // =========================================================================
957
966
 
958
967
  /**
959
- * Returns true if this media is a list (has `list` marker tag).
960
- * Returns false if scalar (no `list` marker = default).
968
+ * Returns true if this media URN describes list-type data (has `list` marker tag).
969
+ * This is a semantic type check it means "the data format IS a list/array".
970
+ * This does NOT indicate input cardinality (single vs multiple items).
971
+ * Cardinality is tracked by is_sequence on the wire protocol, not by URN tags.
961
972
  * @returns {boolean}
962
973
  */
963
974
  isList() { return this._hasMarkerTag('list'); }
@@ -1027,6 +1038,22 @@ class MediaUrn {
1027
1038
  /** @returns {boolean} True if the "bool" marker tag is present */
1028
1039
  isBool() { return this._urn.getTag('bool') !== undefined; }
1029
1040
 
1041
+ /**
1042
+ * Returns true if this media URN describes YAML representation.
1043
+ * @returns {boolean}
1044
+ */
1045
+ isYaml() {
1046
+ return this._hasMarkerTag('yaml');
1047
+ }
1048
+
1049
+ /**
1050
+ * Returns true if this media URN describes CSV representation.
1051
+ * @returns {boolean}
1052
+ */
1053
+ isCsv() {
1054
+ return this._hasMarkerTag('csv');
1055
+ }
1056
+
1030
1057
  /**
1031
1058
  * Check if this represents a single file path type (not array).
1032
1059
  * Returns true if the "file-path" marker tag is present AND no list marker.
@@ -1048,6 +1075,13 @@ class MediaUrn {
1048
1075
  */
1049
1076
  isAnyFilePath() { return this._hasMarkerTag('file-path'); }
1050
1077
 
1078
+ /**
1079
+ * Check if this represents a collection type.
1080
+ * Returns true if the "collection" marker tag is present.
1081
+ * @returns {boolean}
1082
+ */
1083
+ isCollection() { return this._hasMarkerTag('collection'); }
1084
+
1051
1085
  /**
1052
1086
  * Check if this media URN conforms to another (pattern).
1053
1087
  * @param {MediaUrn} pattern
@@ -1123,20 +1157,67 @@ class MediaUrn {
1123
1157
  // =============================================================================
1124
1158
 
1125
1159
  /**
1126
- * Build URN for LLM conversation capability
1127
- * @param {string} langCode - Language code (e.g., "en", "fr")
1160
+ * Build URN for LLM generate-text capability
1128
1161
  * @returns {CapUrn}
1129
1162
  */
1130
- function llmConversationUrn(langCode) {
1163
+ function llmGenerateTextUrn() {
1131
1164
  return new CapUrnBuilder()
1132
- .tag('op', 'conversation')
1133
- .tag('unconstrained', '*')
1134
- .tag('language', langCode)
1165
+ .tag('op', 'generate_text')
1166
+ .tag('llm', '*')
1167
+ .tag('ml-model', '*')
1135
1168
  .inSpec(MEDIA_STRING)
1136
- .outSpec(MEDIA_LLM_INFERENCE_OUTPUT)
1169
+ .outSpec(MEDIA_STRING)
1170
+ .build();
1171
+ }
1172
+
1173
+ /**
1174
+ * Build URN for render-page-image capability
1175
+ * @param {string} inputMedia - The input media URN string
1176
+ * @returns {CapUrn}
1177
+ */
1178
+ function renderPageImageUrn(inputMedia) {
1179
+ return new CapUrnBuilder()
1180
+ .tag('op', 'render_page_image')
1181
+ .inSpec(inputMedia)
1182
+ .outSpec(MEDIA_PNG)
1183
+ .build();
1184
+ }
1185
+
1186
+ /**
1187
+ * Build URN for format conversion capability
1188
+ * @param {string} inMedia - The input media URN string
1189
+ * @param {string} outMedia - The output media URN string
1190
+ * @returns {CapUrn}
1191
+ */
1192
+ function formatConversionUrn(inMedia, outMedia) {
1193
+ return new CapUrnBuilder()
1194
+ .tag('op', 'convert_format')
1195
+ .inSpec(inMedia)
1196
+ .outSpec(outMedia)
1137
1197
  .build();
1138
1198
  }
1139
1199
 
1200
+ /**
1201
+ * Map a primitive type name to the corresponding media URN string.
1202
+ * @param {string} typeName - The type name (e.g., 'string', 'integer', 'string-list')
1203
+ * @returns {string|null} The media URN string, or null if not recognized
1204
+ */
1205
+ function mediaUrnForType(typeName) {
1206
+ switch (typeName) {
1207
+ case 'string': return MEDIA_STRING;
1208
+ case 'integer': return MEDIA_INTEGER;
1209
+ case 'number': return MEDIA_NUMBER;
1210
+ case 'boolean': return MEDIA_BOOLEAN;
1211
+ case 'object': return MEDIA_OBJECT;
1212
+ case 'string-list': return MEDIA_STRING_LIST;
1213
+ case 'integer-list': return MEDIA_INTEGER_LIST;
1214
+ case 'number-list': return MEDIA_NUMBER_LIST;
1215
+ case 'boolean-list': return MEDIA_BOOLEAN_LIST;
1216
+ case 'object-list': return MEDIA_OBJECT_LIST;
1217
+ default: return null;
1218
+ }
1219
+ }
1220
+
1140
1221
  /**
1141
1222
  * Build URN for model-availability capability
1142
1223
  * @returns {CapUrn}
@@ -2933,6 +3014,56 @@ class CapValidator {
2933
3014
  // CAP ARGUMENT VALUE - Unified argument type
2934
3015
  // ============================================================================
2935
3016
 
3017
+ /**
3018
+ * Result from a cap execution.
3019
+ *
3020
+ * Scalar outputs carry raw materialized bytes (e.g. UTF-8 text, raw binary).
3021
+ * List outputs carry a CBOR sequence of values, one per list item.
3022
+ * Empty represents a void cap with no output.
3023
+ */
3024
+ class CapResult {
3025
+ static KIND_SCALAR = 'scalar';
3026
+ static KIND_LIST = 'list';
3027
+ static KIND_EMPTY = 'empty';
3028
+
3029
+ /**
3030
+ * @param {'scalar'|'list'|'empty'} kind
3031
+ * @param {Uint8Array|null} data - Bytes for scalar or CBOR sequence for list, null for empty
3032
+ */
3033
+ constructor(kind, data = null) {
3034
+ this.kind = kind;
3035
+ this.data = data;
3036
+ }
3037
+
3038
+ /** Create a CapResult carrying raw bytes (scalar output). */
3039
+ static scalar(data) {
3040
+ const bytes = data instanceof Uint8Array ? data : new Uint8Array(data || []);
3041
+ return new CapResult(CapResult.KIND_SCALAR, bytes);
3042
+ }
3043
+
3044
+ /** Create a CapResult carrying a CBOR sequence (list output). */
3045
+ static list(cborSequence) {
3046
+ const bytes = cborSequence instanceof Uint8Array ? cborSequence : new Uint8Array(cborSequence || []);
3047
+ return new CapResult(CapResult.KIND_LIST, bytes);
3048
+ }
3049
+
3050
+ /** Create a CapResult for void caps. */
3051
+ static empty() {
3052
+ return new CapResult(CapResult.KIND_EMPTY, null);
3053
+ }
3054
+
3055
+ /** Returns true if this is a scalar result. */
3056
+ isScalar() { return this.kind === CapResult.KIND_SCALAR; }
3057
+
3058
+ /** Returns true if this is a list result. */
3059
+ isList() { return this.kind === CapResult.KIND_LIST; }
3060
+
3061
+ /** Returns true if this is an empty result. */
3062
+ isEmpty() { return this.kind === CapResult.KIND_EMPTY; }
3063
+ }
3064
+
3065
+ // ============================================================================
3066
+
2936
3067
  /**
2937
3068
  * Unified argument type - arguments are identified by media_urn.
2938
3069
  * The cap definition's sources specify how to extract values (stdin, position, cli_flag).
@@ -3189,7 +3320,7 @@ class CompositeCapSet {
3189
3320
  * Execute a capability by finding the best match and delegating
3190
3321
  * @param {string} capUrn - The capability URN to execute
3191
3322
  * @param {CapArgumentValue[]} args - Arguments identified by media_urn
3192
- * @returns {Promise<{binaryOutput: Uint8Array|null, textOutput: string|null}>}
3323
+ * @returns {Promise<CapResult>}
3193
3324
  */
3194
3325
  async executeCap(capUrn, args) {
3195
3326
  let request;
@@ -5435,18 +5566,20 @@ module.exports = {
5435
5566
  MEDIA_NUMBER,
5436
5567
  MEDIA_BOOLEAN,
5437
5568
  MEDIA_OBJECT,
5438
- MEDIA_STRING_ARRAY,
5439
- MEDIA_INTEGER_ARRAY,
5440
- MEDIA_NUMBER_ARRAY,
5441
- MEDIA_BOOLEAN_ARRAY,
5442
- MEDIA_OBJECT_ARRAY,
5569
+ // List types
5570
+ MEDIA_LIST,
5571
+ MEDIA_TEXTABLE_LIST,
5572
+ MEDIA_STRING_LIST,
5573
+ MEDIA_INTEGER_LIST,
5574
+ MEDIA_NUMBER_LIST,
5575
+ MEDIA_BOOLEAN_LIST,
5576
+ MEDIA_OBJECT_LIST,
5443
5577
  MEDIA_IDENTITY,
5444
5578
  MEDIA_VOID,
5445
5579
  MEDIA_PNG,
5446
5580
  MEDIA_AUDIO,
5447
5581
  MEDIA_VIDEO,
5448
5582
  MEDIA_AUDIO_SPEECH,
5449
- MEDIA_IMAGE_THUMBNAIL,
5450
5583
  // Document types (PRIMARY naming)
5451
5584
  MEDIA_PDF,
5452
5585
  MEDIA_EPUB,
@@ -5460,11 +5593,22 @@ module.exports = {
5460
5593
  MEDIA_JSON,
5461
5594
  MEDIA_JSON_SCHEMA,
5462
5595
  MEDIA_YAML,
5596
+ // Format-specific variants
5597
+ MEDIA_JSON_VALUE,
5598
+ MEDIA_JSON_RECORD,
5599
+ MEDIA_JSON_LIST,
5600
+ MEDIA_JSON_LIST_RECORD,
5601
+ MEDIA_YAML_VALUE,
5602
+ MEDIA_YAML_RECORD,
5603
+ MEDIA_YAML_LIST,
5604
+ MEDIA_YAML_LIST_RECORD,
5605
+ MEDIA_CSV,
5606
+ MEDIA_CSV_LIST,
5463
5607
  MEDIA_MODEL_SPEC,
5464
5608
  MEDIA_MODEL_REPO,
5465
5609
  MEDIA_MODEL_DIM,
5466
5610
  MEDIA_DECISION,
5467
- MEDIA_DECISION_ARRAY,
5611
+ MEDIA_TEXTABLE_PAGE,
5468
5612
  // Semantic output types - model management
5469
5613
  MEDIA_DOWNLOAD_OUTPUT,
5470
5614
  MEDIA_LIST_OUTPUT,
@@ -5475,21 +5619,24 @@ module.exports = {
5475
5619
  // Semantic output types - inference
5476
5620
  MEDIA_EMBEDDING_VECTOR,
5477
5621
  MEDIA_LLM_INFERENCE_OUTPUT,
5478
- MEDIA_FILE_METADATA,
5479
- MEDIA_DOCUMENT_OUTLINE,
5480
- MEDIA_DISBOUND_PAGE,
5481
5622
  MEDIA_IMAGE_DESCRIPTION,
5482
5623
  MEDIA_TRANSCRIPTION_OUTPUT,
5483
5624
  // File path types
5484
5625
  MEDIA_FILE_PATH,
5485
5626
  MEDIA_FILE_PATH_ARRAY,
5486
- // Semantic text input types
5487
- MEDIA_FRONTMATTER_TEXT,
5488
5627
  MEDIA_MLX_MODEL_PATH,
5628
+ // Collection types
5629
+ MEDIA_COLLECTION,
5630
+ MEDIA_COLLECTION_LIST,
5631
+ // Cap execution result
5632
+ CapResult,
5489
5633
  // Unified argument type
5490
5634
  CapArgumentValue,
5491
5635
  // Standard cap URN builders
5492
- llmConversationUrn,
5636
+ llmGenerateTextUrn,
5637
+ renderPageImageUrn,
5638
+ formatConversionUrn,
5639
+ mediaUrnForType,
5493
5640
  modelAvailabilityUrn,
5494
5641
  modelPathUrn,
5495
5642
  CapMatrixError,
package/capdag.test.js CHANGED
@@ -13,12 +13,12 @@ const {
13
13
  StdinSource, StdinSourceKind,
14
14
  validateNoMediaSpecRedefinitionSync,
15
15
  CapArgumentValue,
16
- llmConversationUrn, modelAvailabilityUrn, modelPathUrn,
16
+ llmGenerateTextUrn, modelAvailabilityUrn, modelPathUrn,
17
17
  MachineSyntaxError, MachineSyntaxErrorCodes, MachineEdge, Machine, MachineBuilder, parseMachine, parseMachineWithAST,
18
18
  CapRegistryEntry, MediaRegistryEntry, CapRegistryClient,
19
19
  MEDIA_STRING, MEDIA_INTEGER, MEDIA_NUMBER, MEDIA_BOOLEAN,
20
- MEDIA_OBJECT, MEDIA_STRING_ARRAY, MEDIA_INTEGER_ARRAY,
21
- MEDIA_NUMBER_ARRAY, MEDIA_BOOLEAN_ARRAY, MEDIA_OBJECT_ARRAY,
20
+ MEDIA_OBJECT, MEDIA_STRING_LIST, MEDIA_INTEGER_LIST,
21
+ MEDIA_NUMBER_LIST, MEDIA_BOOLEAN_LIST, MEDIA_OBJECT_LIST,
22
22
  MEDIA_IDENTITY, MEDIA_VOID, MEDIA_PNG, MEDIA_AUDIO, MEDIA_VIDEO,
23
23
  MEDIA_PDF, MEDIA_EPUB, MEDIA_MD, MEDIA_TXT, MEDIA_RST, MEDIA_LOG,
24
24
  MEDIA_HTML, MEDIA_XML, MEDIA_JSON, MEDIA_YAML, MEDIA_JSON_SCHEMA,
@@ -26,8 +26,8 @@ const {
26
26
  MEDIA_LLM_INFERENCE_OUTPUT,
27
27
  MEDIA_FILE_PATH, MEDIA_FILE_PATH_ARRAY,
28
28
  MEDIA_COLLECTION, MEDIA_COLLECTION_LIST,
29
- MEDIA_DECISION, MEDIA_DECISION_ARRAY,
30
- MEDIA_AUDIO_SPEECH, MEDIA_IMAGE_THUMBNAIL
29
+ MEDIA_DECISION,
30
+ MEDIA_AUDIO_SPEECH
31
31
  } = require('./capdag.js');
32
32
 
33
33
  // ============================================================================
@@ -827,7 +827,7 @@ function test061_isBinary() {
827
827
  assert(!MediaUrn.fromString(MEDIA_MD).isBinary(), 'MEDIA_MD should not be binary');
828
828
  }
829
829
 
830
- // TEST062: isMap true for MEDIA_OBJECT (record); false for MEDIA_STRING (form=scalar), MEDIA_STRING_ARRAY (list)
830
+ // TEST062: isMap true for MEDIA_OBJECT (record); false for MEDIA_STRING (form=scalar), MEDIA_STRING_LIST (list)
831
831
  // TEST062: is_record returns true if record marker tag is present (key-value structure)
832
832
  function test062_isRecord() {
833
833
  assert(MediaUrn.fromString(MEDIA_OBJECT).isRecord(), 'MEDIA_OBJECT should be record');
@@ -836,7 +836,7 @@ function test062_isRecord() {
836
836
  // Without record marker, is_record is false
837
837
  assert(!MediaUrn.fromString('media:textable').isRecord(), 'plain textable should not be record');
838
838
  assert(!MediaUrn.fromString(MEDIA_STRING).isRecord(), 'MEDIA_STRING should not be record');
839
- assert(!MediaUrn.fromString(MEDIA_STRING_ARRAY).isRecord(), 'MEDIA_STRING_ARRAY should not be record');
839
+ assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isRecord(), 'MEDIA_STRING_LIST should not be record');
840
840
  }
841
841
 
842
842
  // TEST063: is_scalar returns true if NO list marker (scalar is default cardinality)
@@ -848,16 +848,16 @@ function test063_isScalar() {
848
848
  assert(MediaUrn.fromString(MEDIA_OBJECT).isScalar(), 'MEDIA_OBJECT (record but scalar) should be scalar');
849
849
  assert(MediaUrn.fromString('media:textable').isScalar(), 'plain textable should be scalar');
850
850
  // With list marker, is_scalar is false
851
- assert(!MediaUrn.fromString(MEDIA_STRING_ARRAY).isScalar(), 'MEDIA_STRING_ARRAY should not be scalar');
852
- assert(!MediaUrn.fromString(MEDIA_OBJECT_ARRAY).isScalar(), 'MEDIA_OBJECT_ARRAY should not be scalar');
851
+ assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isScalar(), 'MEDIA_STRING_LIST should not be scalar');
852
+ assert(!MediaUrn.fromString(MEDIA_OBJECT_LIST).isScalar(), 'MEDIA_OBJECT_LIST should not be scalar');
853
853
  }
854
854
 
855
- // TEST064: isList true for MEDIA_STRING_ARRAY, MEDIA_INTEGER_ARRAY, MEDIA_OBJECT_ARRAY;
855
+ // TEST064: isList true for MEDIA_STRING_LIST, MEDIA_INTEGER_LIST, MEDIA_OBJECT_LIST;
856
856
  // false for MEDIA_STRING, MEDIA_OBJECT
857
857
  function test064_isList() {
858
- assert(MediaUrn.fromString(MEDIA_STRING_ARRAY).isList(), 'MEDIA_STRING_ARRAY should be list');
859
- assert(MediaUrn.fromString(MEDIA_INTEGER_ARRAY).isList(), 'MEDIA_INTEGER_ARRAY should be list');
860
- assert(MediaUrn.fromString(MEDIA_OBJECT_ARRAY).isList(), 'MEDIA_OBJECT_ARRAY should be list');
858
+ assert(MediaUrn.fromString(MEDIA_STRING_LIST).isList(), 'MEDIA_STRING_LIST should be list');
859
+ assert(MediaUrn.fromString(MEDIA_INTEGER_LIST).isList(), 'MEDIA_INTEGER_LIST should be list');
860
+ assert(MediaUrn.fromString(MEDIA_OBJECT_LIST).isList(), 'MEDIA_OBJECT_LIST should be list');
861
861
  assert(!MediaUrn.fromString(MEDIA_STRING).isList(), 'MEDIA_STRING should not be list');
862
862
  assert(!MediaUrn.fromString(MEDIA_OBJECT).isList(), 'MEDIA_OBJECT should not be list');
863
863
  }
@@ -865,7 +865,7 @@ function test064_isList() {
865
865
  // TEST065: is_opaque returns true if NO record marker (opaque is default structure)
866
866
  function test065_isOpaque() {
867
867
  assert(MediaUrn.fromString(MEDIA_STRING).isOpaque(), 'MEDIA_STRING should be opaque');
868
- assert(MediaUrn.fromString(MEDIA_STRING_ARRAY).isOpaque(), 'MEDIA_STRING_ARRAY (list but no record) should be opaque');
868
+ assert(MediaUrn.fromString(MEDIA_STRING_LIST).isOpaque(), 'MEDIA_STRING_LIST (list but no record) should be opaque');
869
869
  assert(MediaUrn.fromString(MEDIA_PDF).isOpaque(), 'MEDIA_PDF should be opaque');
870
870
  assert(MediaUrn.fromString('media:textable').isOpaque(), 'plain textable should be opaque');
871
871
  // With record marker, is_opaque is false
@@ -912,8 +912,8 @@ function test071_toStringRoundtrip() {
912
912
  function test072_constantsParse() {
913
913
  const constants = [
914
914
  MEDIA_STRING, MEDIA_INTEGER, MEDIA_NUMBER, MEDIA_BOOLEAN,
915
- MEDIA_OBJECT, MEDIA_STRING_ARRAY, MEDIA_INTEGER_ARRAY,
916
- MEDIA_NUMBER_ARRAY, MEDIA_BOOLEAN_ARRAY, MEDIA_OBJECT_ARRAY,
915
+ MEDIA_OBJECT, MEDIA_STRING_LIST, MEDIA_INTEGER_LIST,
916
+ MEDIA_NUMBER_LIST, MEDIA_BOOLEAN_LIST, MEDIA_OBJECT_LIST,
917
917
  MEDIA_IDENTITY, MEDIA_VOID, MEDIA_PNG, MEDIA_PDF, MEDIA_EPUB,
918
918
  MEDIA_MD, MEDIA_TXT, MEDIA_RST, MEDIA_LOG, MEDIA_HTML, MEDIA_XML,
919
919
  MEDIA_JSON, MEDIA_YAML, MEDIA_JSON_SCHEMA, MEDIA_AUDIO, MEDIA_VIDEO,
@@ -1052,8 +1052,8 @@ function test101_resolvedIsScalar() {
1052
1052
 
1053
1053
  // TEST102: MediaSpec with list -> isList() true
1054
1054
  function test102_resolvedIsList() {
1055
- const spec = new MediaSpec('text/plain', null, null, 'String Array', null, MEDIA_STRING_ARRAY);
1056
- assert(spec.isList(), 'Resolved string_array spec should be list');
1055
+ const spec = new MediaSpec('text/plain', null, null, 'String List', null, MEDIA_STRING_LIST);
1056
+ assert(spec.isList(), 'Resolved string_list spec should be list');
1057
1057
  }
1058
1058
 
1059
1059
  // TEST103: MediaSpec with json tag -> isJSON() true
@@ -1706,37 +1706,37 @@ function test309_modelAvailabilityAndPathAreDistinct() {
1706
1706
  assert(avail.toString() !== path.toString(), 'availability and path must be distinct');
1707
1707
  }
1708
1708
 
1709
- // TEST310: llm_conversation_urn uses unconstrained tag (not constrained)
1710
- function test310_llmConversationUrnUnconstrained() {
1711
- const urn = llmConversationUrn('en');
1712
- assert(urn.getTag('unconstrained') !== undefined, 'Must have unconstrained tag');
1713
- assert(urn.hasTag('op', 'conversation'), 'Must have op=conversation');
1714
- assert(urn.hasTag('language', 'en'), 'Must have language=en');
1709
+ // TEST310: llm_generate_text_urn has correct op and ml-model tags
1710
+ function test310_llmGenerateTextUrn() {
1711
+ const urn = llmGenerateTextUrn();
1712
+ assert(urn.hasTag('op', 'generate_text'), 'Must have op=generate_text');
1713
+ assert(urn.getTag('llm') !== undefined, 'Must have llm tag');
1714
+ assert(urn.getTag('ml-model') !== undefined, 'Must have ml-model tag');
1715
1715
  }
1716
1716
 
1717
- // TEST311: llm_conversation_urn in/out specs match the expected media URNs semantically
1718
- function test311_llmConversationUrnSpecs() {
1719
- const urn = llmConversationUrn('fr');
1717
+ // TEST311: llm_generate_text_urn in/out specs match MEDIA_STRING
1718
+ function test311_llmGenerateTextUrnSpecs() {
1719
+ const urn = llmGenerateTextUrn();
1720
1720
  const inSpec = TaggedUrn.fromString(urn.getInSpec());
1721
1721
  const expectedIn = TaggedUrn.fromString(MEDIA_STRING);
1722
1722
  assert(inSpec.conformsTo(expectedIn), 'in_spec must conform to MEDIA_STRING');
1723
1723
  const outSpec = TaggedUrn.fromString(urn.getOutSpec());
1724
- const expectedOut = TaggedUrn.fromString(MEDIA_LLM_INFERENCE_OUTPUT);
1725
- assert(outSpec.conformsTo(expectedOut), 'out_spec must conform to MEDIA_LLM_INFERENCE_OUTPUT');
1724
+ const expectedOut = TaggedUrn.fromString(MEDIA_STRING);
1725
+ assert(outSpec.conformsTo(expectedOut), 'out_spec must conform to MEDIA_STRING');
1726
1726
  }
1727
1727
 
1728
1728
  // TEST312: All URN builders produce parseable cap URNs
1729
1729
  function test312_allUrnBuildersProduceValidUrns() {
1730
1730
  const avail = modelAvailabilityUrn();
1731
1731
  const path = modelPathUrn();
1732
- const conv = llmConversationUrn('en');
1732
+ const llmGen = llmGenerateTextUrn();
1733
1733
 
1734
1734
  const parsedAvail = CapUrn.fromString(avail.toString());
1735
1735
  assert(parsedAvail !== null, 'modelAvailabilityUrn must be parseable');
1736
1736
  const parsedPath = CapUrn.fromString(path.toString());
1737
1737
  assert(parsedPath !== null, 'modelPathUrn must be parseable');
1738
- const parsedConv = CapUrn.fromString(conv.toString());
1739
- assert(parsedConv !== null, 'llmConversationUrn must be parseable');
1738
+ const parsedLlmGen = CapUrn.fromString(llmGen.toString());
1739
+ assert(parsedLlmGen !== null, 'llmGenerateTextUrn must be parseable');
1740
1740
  }
1741
1741
 
1742
1742
  // ============================================================================
@@ -2398,7 +2398,7 @@ function test335_cartridgeRepoServerClientIntegration() {
2398
2398
  // TEST546: isImage returns true only when image marker tag is present
2399
2399
  function test546_isImage() {
2400
2400
  assert(MediaUrn.fromString(MEDIA_PNG).isImage(), 'MEDIA_PNG should be image');
2401
- assert(MediaUrn.fromString(MEDIA_IMAGE_THUMBNAIL).isImage(), 'MEDIA_IMAGE_THUMBNAIL should be image');
2401
+ assert(MediaUrn.fromString('media:image;png;thumbnail').isImage(), 'media:image;png;thumbnail should be image');
2402
2402
  assert(MediaUrn.fromString('media:image;jpg').isImage(), 'media:image;jpg should be image');
2403
2403
  // Non-image types
2404
2404
  assert(!MediaUrn.fromString(MEDIA_PDF).isImage(), 'MEDIA_PDF should not be image');
@@ -2432,8 +2432,8 @@ function test548_isVideo() {
2432
2432
  function test549_isNumeric() {
2433
2433
  assert(MediaUrn.fromString(MEDIA_INTEGER).isNumeric(), 'MEDIA_INTEGER should be numeric');
2434
2434
  assert(MediaUrn.fromString(MEDIA_NUMBER).isNumeric(), 'MEDIA_NUMBER should be numeric');
2435
- assert(MediaUrn.fromString(MEDIA_INTEGER_ARRAY).isNumeric(), 'MEDIA_INTEGER_ARRAY should be numeric');
2436
- assert(MediaUrn.fromString(MEDIA_NUMBER_ARRAY).isNumeric(), 'MEDIA_NUMBER_ARRAY should be numeric');
2435
+ assert(MediaUrn.fromString(MEDIA_INTEGER_LIST).isNumeric(), 'MEDIA_INTEGER_LIST should be numeric');
2436
+ assert(MediaUrn.fromString(MEDIA_NUMBER_LIST).isNumeric(), 'MEDIA_NUMBER_LIST should be numeric');
2437
2437
  // Non-numeric types
2438
2438
  assert(!MediaUrn.fromString(MEDIA_STRING).isNumeric(), 'MEDIA_STRING should not be numeric');
2439
2439
  assert(!MediaUrn.fromString(MEDIA_BOOLEAN).isNumeric(), 'MEDIA_BOOLEAN should not be numeric');
@@ -2443,9 +2443,9 @@ function test549_isNumeric() {
2443
2443
  // TEST550: isBool returns true only when bool marker tag is present
2444
2444
  function test550_isBool() {
2445
2445
  assert(MediaUrn.fromString(MEDIA_BOOLEAN).isBool(), 'MEDIA_BOOLEAN should be bool');
2446
- assert(MediaUrn.fromString(MEDIA_BOOLEAN_ARRAY).isBool(), 'MEDIA_BOOLEAN_ARRAY should be bool');
2447
- assert(MediaUrn.fromString(MEDIA_DECISION).isBool(), 'MEDIA_DECISION should be bool');
2448
- assert(MediaUrn.fromString(MEDIA_DECISION_ARRAY).isBool(), 'MEDIA_DECISION_ARRAY should be bool');
2446
+ assert(MediaUrn.fromString(MEDIA_BOOLEAN_LIST).isBool(), 'MEDIA_BOOLEAN_LIST should be bool');
2447
+ // MEDIA_DECISION is now a JSON record type (not bool)
2448
+ assert(!MediaUrn.fromString(MEDIA_DECISION).isBool(), 'MEDIA_DECISION should not be bool (it is a JSON record now)');
2449
2449
  // Non-bool types
2450
2450
  assert(!MediaUrn.fromString(MEDIA_STRING).isBool(), 'MEDIA_STRING should not be bool');
2451
2451
  assert(!MediaUrn.fromString(MEDIA_INTEGER).isBool(), 'MEDIA_INTEGER should not be bool');
@@ -2468,7 +2468,7 @@ function test552_isFilePathArray() {
2468
2468
  // Scalar file-path is NOT isFilePathArray
2469
2469
  assert(!MediaUrn.fromString(MEDIA_FILE_PATH).isFilePathArray(), 'MEDIA_FILE_PATH should not be isFilePathArray');
2470
2470
  // Non-file-path types
2471
- assert(!MediaUrn.fromString(MEDIA_STRING_ARRAY).isFilePathArray(), 'MEDIA_STRING_ARRAY should not be file-path-array');
2471
+ assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isFilePathArray(), 'MEDIA_STRING_LIST should not be file-path-array');
2472
2472
  }
2473
2473
 
2474
2474
  // TEST553: isAnyFilePath returns true for both scalar and array file-path
@@ -2477,7 +2477,7 @@ function test553_isAnyFilePath() {
2477
2477
  assert(MediaUrn.fromString(MEDIA_FILE_PATH_ARRAY).isAnyFilePath(), 'MEDIA_FILE_PATH_ARRAY should be any-file-path');
2478
2478
  // Non-file-path types
2479
2479
  assert(!MediaUrn.fromString(MEDIA_STRING).isAnyFilePath(), 'MEDIA_STRING should not be any-file-path');
2480
- assert(!MediaUrn.fromString(MEDIA_STRING_ARRAY).isAnyFilePath(), 'MEDIA_STRING_ARRAY should not be any-file-path');
2480
+ assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isAnyFilePath(), 'MEDIA_STRING_LIST should not be any-file-path');
2481
2481
  }
2482
2482
 
2483
2483
  // TEST554: isCollection returns true when collection marker tag is present
@@ -5408,8 +5408,8 @@ async function runTests() {
5408
5408
  runTest('TEST307: model_availability_urn', test307_modelAvailabilityUrn);
5409
5409
  runTest('TEST308: model_path_urn', test308_modelPathUrn);
5410
5410
  runTest('TEST309: model_availability_and_path_are_distinct', test309_modelAvailabilityAndPathAreDistinct);
5411
- runTest('TEST310: llm_conversation_urn_unconstrained', test310_llmConversationUrnUnconstrained);
5412
- runTest('TEST311: llm_conversation_urn_specs', test311_llmConversationUrnSpecs);
5411
+ runTest('TEST310: llm_generate_text_urn', test310_llmGenerateTextUrn);
5412
+ runTest('TEST311: llm_generate_text_urn_specs', test311_llmGenerateTextUrnSpecs);
5413
5413
  runTest('TEST312: all_urn_builders_produce_valid_urns', test312_allUrnBuildersProduceValidUrns);
5414
5414
 
5415
5415
  // JS-specific tests (no Rust number)
package/package.json CHANGED
@@ -40,5 +40,5 @@
40
40
  "pretest": "npm run build:parser",
41
41
  "test": "node capdag.test.js"
42
42
  },
43
- "version": "0.126.278"
43
+ "version": "0.127.280"
44
44
  }