papiers 0.1.3 → 0.1.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/README.md CHANGED
@@ -31,7 +31,8 @@ you wrote, the conversations you had — instead of generic web knowledge.
31
31
  The package ships a ready-to-use agent skill at
32
32
  [`skills/papiers-research/SKILL.md`](skills/papiers-research/SKILL.md). It
33
33
  encodes the loop — survey what you've been reading, search across passages +
34
- highlights + conversations in one ranking, then read and cite your own sources.
34
+ highlights + notes + conversations in one ranking, then read and cite your own
35
+ sources.
35
36
  Hand that file to your agent (or drop it in its skills directory) and ask it to
36
37
  propose an idea, design an experiment, or review a draft. It will think with
37
38
  your library, not around it.
package/dist/index.js CHANGED
@@ -412,13 +412,41 @@ function isNodeError(error) {
412
412
  return Boolean(error && typeof error === "object" && "code" in error);
413
413
  }
414
414
 
415
+ // ../../src/shared/papiers-resource-locators.ts
416
+ var UUID_PATTERN = "[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}";
417
+ var PAPIERS_RESOURCE_LOCATOR_PATTERN_SOURCE = `papiers://(?:note|highlight|item|conversation|artifact)/${UUID_PATTERN}`;
418
+ var RESOURCE_LOCATOR_PATTERN = new RegExp(
419
+ `^papiers://(note|highlight|item|conversation|artifact)/(${UUID_PATTERN})$`,
420
+ "u"
421
+ );
422
+ var UUID_REGEX = new RegExp(`^${UUID_PATTERN}$`, "iu");
423
+ var READ_KIND_BY_LOCATOR_KIND = {
424
+ note: "note",
425
+ highlight: "highlight",
426
+ item: "document",
427
+ conversation: "conversation",
428
+ artifact: "artifact"
429
+ };
430
+ function parsePapiersResourceLocator(value) {
431
+ const match = RESOURCE_LOCATOR_PATTERN.exec(value);
432
+ if (!match) return null;
433
+ const kind = match[1];
434
+ return {
435
+ value,
436
+ kind,
437
+ readKind: READ_KIND_BY_LOCATOR_KIND[kind],
438
+ internalId: match[2].toLowerCase()
439
+ };
440
+ }
441
+
415
442
  // src/document-contract.ts
416
443
  var PUBLIC_KINDS = [
417
444
  "document",
418
445
  "passage",
419
446
  "highlight",
420
447
  "conversation",
421
- "equation"
448
+ "equation",
449
+ "note"
422
450
  ];
423
451
  var EXPECTED_DOCUMENT_READ_FORMATS = [
424
452
  "metadata",
@@ -440,6 +468,10 @@ var EXPECTED_CONVERSATION_READ_FORMATS = [
440
468
  var EXPECTED_EQUATION_READ_FORMATS = [
441
469
  "metadata"
442
470
  ];
471
+ var EXPECTED_NOTE_READ_FORMATS = [
472
+ "markdown",
473
+ "plain"
474
+ ];
443
475
  var DOCUMENT_READ_REQUEST_FORMATS = [
444
476
  ...EXPECTED_DOCUMENT_READ_FORMATS,
445
477
  "auto"
@@ -506,6 +538,7 @@ var PASSAGE_PUBLIC_ID_PATTERN = /^pas_[a-f0-9]{32}$/u;
506
538
  var HIGHLIGHT_PUBLIC_ID_PATTERN = /^hl_[a-f0-9]{32}$/u;
507
539
  var CONVERSATION_PUBLIC_ID_PATTERN = /^cnv_[a-f0-9]{32}$/u;
508
540
  var EQUATION_PUBLIC_ID_PATTERN = /^eq_[a-f0-9]{32}$/u;
541
+ var NOTE_PUBLIC_ID_PATTERN = /^nte_[a-f0-9]{32}$/u;
509
542
  var SEARCH_RANKING_ID_PATTERN = /^srch_[a-f0-9]{32}$/u;
510
543
  function isSearchRankingId(value) {
511
544
  return SEARCH_RANKING_ID_PATTERN.test(value);
@@ -525,6 +558,9 @@ function isConversationPublicId(value) {
525
558
  function isEquationPublicId(value) {
526
559
  return EQUATION_PUBLIC_ID_PATTERN.test(value);
527
560
  }
561
+ function isNotePublicId(value) {
562
+ return NOTE_PUBLIC_ID_PATTERN.test(value);
563
+ }
528
564
  function publicReadKindForId(value) {
529
565
  if (isDocumentPublicId(value)) {
530
566
  return "document";
@@ -541,8 +577,22 @@ function publicReadKindForId(value) {
541
577
  if (isEquationPublicId(value)) {
542
578
  return "equation";
543
579
  }
580
+ if (isNotePublicId(value)) {
581
+ return "note";
582
+ }
544
583
  return null;
545
584
  }
585
+ function readKindForIdentifier(value) {
586
+ const publicKind = publicReadKindForId(value);
587
+ if (publicKind) {
588
+ return publicKind;
589
+ }
590
+ const locatorKind = parsePapiersResourceLocator(value)?.readKind;
591
+ return locatorKind === "artifact" ? null : locatorKind ?? null;
592
+ }
593
+ function isDocumentReadIdentifier(value) {
594
+ return isDocumentPublicId(value) || parsePapiersResourceLocator(value)?.kind === "item";
595
+ }
546
596
  function isPublicIdForKind(value, kind) {
547
597
  return publicReadKindForId(value) === kind;
548
598
  }
@@ -593,7 +643,8 @@ var PAPIERS_SEARCH_QUALITY_SIGNALS = [
593
643
  ];
594
644
  var PAPIERS_SEARCH_RESULT_KINDS = [
595
645
  "document",
596
- "conversation"
646
+ "conversation",
647
+ "note"
597
648
  ];
598
649
  var PAPIERS_SEARCH_EVIDENCE_KINDS = [
599
650
  "passage",
@@ -608,7 +659,8 @@ var PAPIERS_SEARCH_MATCH_REASONS = [
608
659
  "passage",
609
660
  "highlight",
610
661
  "equation",
611
- "conversation"
662
+ "conversation",
663
+ "note"
612
664
  ];
613
665
  var PAPIERS_PUBLIC_NOT_READY_REASONS_BY_FEATURE = {
614
666
  document_content_read: ["explicit_enablement_required"],
@@ -651,6 +703,8 @@ var PAPIERS_PUBLIC_ERROR_CODES = [
651
703
  "invalid_request",
652
704
  "not_found",
653
705
  "source_superseded",
706
+ "content_limit_exceeded",
707
+ "request_limit_exceeded",
654
708
  "rate_limited",
655
709
  "internal_error"
656
710
  ];
@@ -789,6 +843,7 @@ var RANKED_SEARCH_CURSOR_WARNING_MESSAGES = /* @__PURE__ */ new Set([
789
843
  var DOCUMENT_READ_TRUNCATED_WARNING_CODE = "read_truncated";
790
844
  var DOCUMENT_READ_TRUNCATED_WARNING_MESSAGE = "Document content was truncated by public read limits.";
791
845
  var PASSAGE_READ_TRUNCATED_WARNING_MESSAGE = "Passage content was truncated by public read limits.";
846
+ var NOTE_READ_TRUNCATED_WARNING_MESSAGE = "Note content was truncated by public read limits.";
792
847
  var READ_RESPONSE_KEYS = /* @__PURE__ */ new Set([
793
848
  "schemaVersion",
794
849
  "requestId",
@@ -858,6 +913,11 @@ var SEARCH_CONVERSATION_RESULT_KEYS = /* @__PURE__ */ new Set([
858
913
  ...SEARCH_RESULT_BASE_KEYS,
859
914
  "snippet"
860
915
  ]);
916
+ var SEARCH_NOTE_RESULT_KEYS = /* @__PURE__ */ new Set([
917
+ ...SEARCH_RESULT_BASE_KEYS,
918
+ "snippet",
919
+ "metadata"
920
+ ]);
861
921
  var SEARCH_EVIDENCE_KEYS = /* @__PURE__ */ new Set([
862
922
  "kind",
863
923
  "id",
@@ -910,6 +970,19 @@ var READ_SUCCESS_EQUATION_ITEM_KEYS = /* @__PURE__ */ new Set([
910
970
  "format",
911
971
  "metadata"
912
972
  ]);
973
+ var READ_SUCCESS_NOTE_ITEM_KEYS = /* @__PURE__ */ new Set([
974
+ "id",
975
+ "kind",
976
+ "ok",
977
+ "format",
978
+ "content",
979
+ "truncated",
980
+ "charCount",
981
+ "tokenEstimate",
982
+ "metadata",
983
+ "nextCursor",
984
+ "warnings"
985
+ ]);
913
986
  var READ_ERROR_DOCUMENT_ITEM_KEYS = /* @__PURE__ */ new Set(["id", "kind", "ok", "error"]);
914
987
  var READ_ITEM_ERROR_KEYS = /* @__PURE__ */ new Set(["code", "message", "retryable"]);
915
988
  var HIGHLIGHT_READ_METADATA_KEYS = /* @__PURE__ */ new Set([
@@ -919,6 +992,8 @@ var HIGHLIGHT_READ_METADATA_KEYS = /* @__PURE__ */ new Set([
919
992
  "anchorType",
920
993
  "annotationText",
921
994
  "quoteContext",
995
+ "quotePrefix",
996
+ "quoteSuffix",
922
997
  "createdAt",
923
998
  "updatedAt"
924
999
  ]);
@@ -940,6 +1015,14 @@ var EQUATION_READ_METADATA_KEYS = /* @__PURE__ */ new Set([
940
1015
  "nodeType",
941
1016
  "updatedAt"
942
1017
  ]);
1018
+ var NOTE_READ_METADATA_KEYS = /* @__PURE__ */ new Set([
1019
+ "documentId",
1020
+ "attachedTitle",
1021
+ "editedAt",
1022
+ "createdAt",
1023
+ "updatedAt",
1024
+ "title"
1025
+ ]);
943
1026
  var READ_LOCATIONS_KEYS = /* @__PURE__ */ new Set(["pages", "scope", "truncated"]);
944
1027
  var READ_LOCATION_PAGE_KEYS = /* @__PURE__ */ new Set(["page", "sections"]);
945
1028
  var READ_LOCATION_SCOPE_KEYS = /* @__PURE__ */ new Set(["pages"]);
@@ -971,9 +1054,16 @@ var PASSAGE_READ_NOT_FOUND_MESSAGE = "Passage is not available.";
971
1054
  var HIGHLIGHT_READ_NOT_FOUND_MESSAGE = "Highlight is not available.";
972
1055
  var CONVERSATION_READ_NOT_FOUND_MESSAGE = "Conversation is not available.";
973
1056
  var EQUATION_READ_NOT_FOUND_MESSAGE = "Equation is not available.";
1057
+ var NOTE_READ_NOT_FOUND_MESSAGE = "Note is not available.";
974
1058
  var DOCUMENT_READ_SOURCE_SUPERSEDED_CODE = "source_superseded";
1059
+ var CONVERSATION_READ_CONTENT_LIMIT_CODE = "content_limit_exceeded";
1060
+ var CONVERSATION_READ_REQUEST_LIMIT_CODE = "request_limit_exceeded";
975
1061
  var DOCUMENT_READ_SOURCE_SUPERSEDED_MESSAGE = "Document content is not available for the current source.";
1062
+ var NOTE_READ_SOURCE_SUPERSEDED_MESSAGE = "Note content changed since this cursor was issued.";
976
1063
  var PASSAGE_READ_CONTENT_UNAVAILABLE_MESSAGE = "Passage content is not available.";
1064
+ var CONVERSATION_READ_SOURCE_SUPERSEDED_MESSAGE = "Conversation source is no longer available. Search again for a current result.";
1065
+ var CONVERSATION_READ_CONTENT_LIMIT_MESSAGE = "Conversation content exceeds the public read limit.";
1066
+ var CONVERSATION_READ_REQUEST_LIMIT_MESSAGE = "Conversation read batch is too large. Retry this item in a smaller batch.";
977
1067
  var PUBLIC_CURSOR_BASE64URL_PATTERN = /^[A-Za-z0-9_-]{1,4096}$/u;
978
1068
  var PUBLIC_CURSOR_SIGNATURE_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
979
1069
  var PUBLIC_CURSOR_HASH_PATTERN = /^[A-Za-z0-9_-]{43}$/u;
@@ -1033,6 +1123,8 @@ var INTERNAL_RESPONSE_KEYS = /* @__PURE__ */ new Set([
1033
1123
  "latexTokens",
1034
1124
  "library_item_id",
1035
1125
  "libraryItemId",
1126
+ "note_id",
1127
+ "noteId",
1036
1128
  "actor_id",
1037
1129
  "actorId",
1038
1130
  "owner_user_id",
@@ -1056,7 +1148,7 @@ var INTERNAL_RESPONSE_KEYS = /* @__PURE__ */ new Set([
1056
1148
  "last_used_ip_hash",
1057
1149
  "lastUsedIpHash"
1058
1150
  ]);
1059
- var UNSAFE_PUBLIC_EXCERPT_MARKER_PATTERN = /\b(?:artifact_id|artifactId|astNodeId|atom_id|atomId|branchKey|contentHash|conversation_id|conversationId|current_artifact_id|currentArtifactId|current_internal_target_json|currentInternalTargetJson|internal_locator|internalLocator|latexTokens|library_item_id|libraryItemId|actor_id|actorId|owner_user_id|ownerUserId|secret_hash|secretHash|source_id|sourceId|source_url|sourceUrl|source_locator|sourceLocator|source_locator_hash|sourceLocatorHash|source_locator_json|sourceLocatorJson|projectionHash|search_row_id|searchRowId|last_used_ip_hash|lastUsedIpHash)\b/u;
1151
+ var UNSAFE_PUBLIC_EXCERPT_MARKER_PATTERN = /\b(?:artifact_id|artifactId|astNodeId|atom_id|atomId|branchKey|contentHash|conversation_id|conversationId|current_artifact_id|currentArtifactId|current_internal_target_json|currentInternalTargetJson|internal_locator|internalLocator|latexTokens|library_item_id|libraryItemId|note_id|noteId|actor_id|actorId|owner_user_id|ownerUserId|secret_hash|secretHash|source_id|sourceId|source_url|sourceUrl|source_locator|sourceLocator|source_locator_hash|sourceLocatorHash|source_locator_json|sourceLocatorJson|projectionHash|search_row_id|searchRowId|last_used_ip_hash|lastUsedIpHash)\b/u;
1060
1152
  var UNSAFE_PUBLIC_EXCERPT_URL_PATTERN = /\bhttps?:\/\/\S+[?&](?:access_token|key|secret|sig|signature|token)=/iu;
1061
1153
  var UNSAFE_PUBLIC_EXCERPT_SIGNED_URL_PATTERN = /\bsigned[_-]?url\b/iu;
1062
1154
  var CLI_DOCUMENT_READ_FORMAT_SET = new Set(
@@ -1279,10 +1371,10 @@ function validatePapiersReadResponse(label, body, options = {}) {
1279
1371
  const expectedInclude = options.expectedInclude ? new Set(options.expectedInclude) : void 0;
1280
1372
  for (const [index, rawItem] of items.entries()) {
1281
1373
  const item = assertRecord(`${label} item ${index}`, rawItem);
1282
- const id = assertReadPublicId(`${label} item ${index} id`, item.id);
1374
+ const id = assertReadIdentifier(`${label} item ${index} id`, item.id);
1283
1375
  if (options.expectedIds !== void 0 && id !== options.expectedIds[index]) {
1284
1376
  throw new PapiersCliResponseValidationError(
1285
- `${label} did not echo the requested public ids in order.`
1377
+ `${label} did not echo the requested identifiers in order.`
1286
1378
  );
1287
1379
  }
1288
1380
  const kind = validateReadItemKind(
@@ -1388,10 +1480,16 @@ function validateSearchResults(label, results, options = {}) {
1388
1480
  `${label} result ${index} kind was not one of the requested kinds.`
1389
1481
  );
1390
1482
  }
1391
- if (kind === "document") {
1392
- validateSearchDocumentResult(`${label} result ${index}`, result);
1393
- } else {
1394
- validateSearchConversationResult(`${label} result ${index}`, result);
1483
+ switch (kind) {
1484
+ case "document":
1485
+ validateSearchDocumentResult(`${label} result ${index}`, result);
1486
+ break;
1487
+ case "conversation":
1488
+ validateSearchConversationResult(`${label} result ${index}`, result);
1489
+ break;
1490
+ case "note":
1491
+ validateSearchNoteResult(`${label} result ${index}`, result);
1492
+ break;
1395
1493
  }
1396
1494
  }
1397
1495
  return values.length;
@@ -1412,6 +1510,9 @@ function validateSearchResultKind(label, value, id) {
1412
1510
  if (value === "conversation" && isPublicIdForKind(id, "conversation")) {
1413
1511
  return "conversation";
1414
1512
  }
1513
+ if (value === "note" && isPublicIdForKind(id, "note")) {
1514
+ return "note";
1515
+ }
1415
1516
  throw new PapiersCliResponseValidationError(
1416
1517
  `${label} did not match the public id kind.`
1417
1518
  );
@@ -1465,6 +1566,23 @@ function validateSearchConversationResult(label, result) {
1465
1566
  validateOptionalNullableString(`${label} title`, result.title);
1466
1567
  validateOptionalPublicExcerpt(`${label} snippet`, result.snippet);
1467
1568
  }
1569
+ function validateSearchNoteResult(label, result) {
1570
+ rejectUnexpectedKeys(label, result, SEARCH_NOTE_RESULT_KEYS);
1571
+ const id = assertNonEmptyString(`${label} id`, result.id);
1572
+ if (!isPublicIdForKind(id, "note")) {
1573
+ throw new PapiersCliResponseValidationError(
1574
+ `${label} id was not an nte_* public id.`
1575
+ );
1576
+ }
1577
+ assertPositiveInteger(`${label} rank`, result.rank);
1578
+ validateSearchScore(`${label} score`, result.score);
1579
+ validateSearchMatchedBy(`${label} matchedBy`, result.matchedBy, {
1580
+ allow: /* @__PURE__ */ new Set(["note"])
1581
+ });
1582
+ validateOptionalNullableString(`${label} title`, result.title);
1583
+ validateOptionalPublicExcerpt(`${label} snippet`, result.snippet);
1584
+ validateNoteMetadata(`${label} metadata`, result.metadata, assertDocId);
1585
+ }
1468
1586
  function validateSearchEvidenceList(label, value) {
1469
1587
  const evidence = assertArray(label, value);
1470
1588
  for (const [index, rawEvidence] of evidence.entries()) {
@@ -1666,9 +1784,9 @@ function validateOptionalDocumentIdForKind(label, value, kind) {
1666
1784
  }
1667
1785
  return void 0;
1668
1786
  }
1669
- if (kind === "conversation") {
1787
+ if (kind === "conversation" || kind === "note") {
1670
1788
  throw new PapiersCliResponseValidationError(
1671
- `${label} was not supported for conversation items.`
1789
+ `${label} was not supported for ${kind} items.`
1672
1790
  );
1673
1791
  }
1674
1792
  return assertDocId(label, value);
@@ -1692,12 +1810,15 @@ function validateOptionalListSearchMetadata(label, value, kind) {
1692
1810
  return validatePassageListSearchMetadata(label, value);
1693
1811
  }
1694
1812
  if (kind === "highlight") {
1695
- return validateHighlightReadMetadata(label, value);
1813
+ return validateHighlightMetadata(label, value, assertDocId);
1696
1814
  }
1697
1815
  if (kind === "conversation") {
1698
1816
  validateConversationReadMetadata(label, value);
1699
1817
  return void 0;
1700
1818
  }
1819
+ if (kind === "note") {
1820
+ return validateNoteMetadata(label, value, assertDocId);
1821
+ }
1701
1822
  return validateEquationReadMetadata(label, value);
1702
1823
  }
1703
1824
  function validatePassageListSearchMetadata(label, value) {
@@ -1728,11 +1849,54 @@ function validateReadSuccessItem(label, item, kind, options) {
1728
1849
  return validateConversationReadSuccessItem(label, item, options);
1729
1850
  case "equation":
1730
1851
  return validateEquationReadSuccessItem(label, item, options);
1852
+ case "note":
1853
+ return validateNoteReadSuccessItem(label, item, options);
1731
1854
  }
1732
1855
  }
1856
+ function validateNoteReadSuccessItem(label, item, options) {
1857
+ rejectUnexpectedKeys(label, item, READ_SUCCESS_NOTE_ITEM_KEYS);
1858
+ const itemFormat = validateCliReadFormat(`${label} format`, item.format);
1859
+ if (!EXPECTED_NOTE_READ_FORMATS.some((format) => format === itemFormat)) {
1860
+ throw new PapiersCliResponseValidationError(
1861
+ `${label} format was not supported for note reads.`
1862
+ );
1863
+ }
1864
+ if (options.expectedFormat !== void 0 && !isResolvedReadFormatAllowed(itemFormat, options.expectedFormat)) {
1865
+ throw new PapiersCliResponseValidationError(
1866
+ `${label} did not echo the requested read format.`
1867
+ );
1868
+ }
1869
+ const contentResult = validateReadContent(label, item, itemFormat, {
1870
+ expectedMaxCharsPerItem: options.expectedMaxCharsPerItem
1871
+ });
1872
+ validateReadItemCursor(
1873
+ label,
1874
+ item.nextCursor,
1875
+ itemFormat,
1876
+ contentResult.truncated,
1877
+ options.singleItemRead
1878
+ );
1879
+ validateNoteMetadata(
1880
+ `${label} metadata`,
1881
+ item.metadata,
1882
+ assertDocumentReadIdentifier
1883
+ );
1884
+ const hasReadTruncatedWarning = validateOptionalReadWarnings(
1885
+ `${label} warnings`,
1886
+ item.warnings,
1887
+ itemFormat,
1888
+ "note"
1889
+ );
1890
+ if (hasReadTruncatedWarning && !contentResult.truncated) {
1891
+ throw new PapiersCliResponseValidationError(
1892
+ `${label} returned read_truncated without truncated=true.`
1893
+ );
1894
+ }
1895
+ return { ...contentResult, contentRead: true };
1896
+ }
1733
1897
  function validateDocumentReadSuccessItem(label, item, options) {
1734
1898
  rejectUnexpectedKeys(label, item, READ_SUCCESS_DOCUMENT_ITEM_KEYS);
1735
- assertDocId(`${label} id`, item.id);
1899
+ assertDocumentReadIdentifier(`${label} id`, item.id);
1736
1900
  const itemFormat = validateCliReadFormat(`${label} format`, item.format);
1737
1901
  if (options.expectedFormat !== void 0 && !isResolvedReadFormatAllowed(itemFormat, options.expectedFormat)) {
1738
1902
  throw new PapiersCliResponseValidationError(
@@ -1854,11 +2018,19 @@ function validateHighlightReadSuccessItem(label, item, options) {
1854
2018
  }
1855
2019
  if (itemFormat === "plain") {
1856
2020
  const contentResult = validatePlainReadContentFields(label, item);
1857
- validateHighlightReadMetadata(`${label} metadata`, item.metadata);
2021
+ validateHighlightMetadata(
2022
+ `${label} metadata`,
2023
+ item.metadata,
2024
+ assertDocumentReadIdentifier
2025
+ );
1858
2026
  return { ...contentResult, contentRead: true };
1859
2027
  }
1860
2028
  rejectMetadataOnlyContentFields(label, item);
1861
- validateHighlightReadMetadata(`${label} metadata`, item.metadata);
2029
+ validateHighlightMetadata(
2030
+ `${label} metadata`,
2031
+ item.metadata,
2032
+ assertDocumentReadIdentifier
2033
+ );
1862
2034
  return { returnedChars: 0, truncated: false, contentRead: false };
1863
2035
  }
1864
2036
  function validateConversationReadSuccessItem(label, item, options) {
@@ -1900,18 +2072,21 @@ function validateEquationReadSuccessItem(label, item, options) {
1900
2072
  return { returnedChars: 0, truncated: false, contentRead: false };
1901
2073
  }
1902
2074
  function validateReadItemKind(label, value, id) {
1903
- const expectedKind = publicReadKindForId(id);
1904
- if (expectedKind === null || value !== expectedKind || value !== "document" && value !== "passage" && value !== "highlight" && value !== "conversation" && value !== "equation") {
2075
+ const expectedKind = readKindForIdentifier(id);
2076
+ if (expectedKind === null || value !== expectedKind || value !== "document" && value !== "passage" && value !== "highlight" && value !== "conversation" && value !== "equation" && value !== "note") {
1905
2077
  throw new PapiersCliResponseValidationError(
1906
- `${label} did not match the public id kind.`
2078
+ `${label} did not match the read identifier kind.`
1907
2079
  );
1908
2080
  }
1909
2081
  return value;
1910
2082
  }
1911
- function validateHighlightReadMetadata(label, value) {
2083
+ function validateHighlightMetadata(label, value, validateDocumentId) {
1912
2084
  const metadata = assertRecord(label, value);
1913
2085
  rejectUnexpectedKeys(label, metadata, HIGHLIGHT_READ_METADATA_KEYS);
1914
- const documentId = assertDocId(`${label}.documentId`, metadata.documentId);
2086
+ const documentId = validateDocumentId(
2087
+ `${label}.documentId`,
2088
+ metadata.documentId
2089
+ );
1915
2090
  validateOptionalNullableString(
1916
2091
  `${label}.documentTitle`,
1917
2092
  metadata.documentTitle
@@ -1930,6 +2105,8 @@ function validateHighlightReadMetadata(label, value) {
1930
2105
  `${label}.quoteContext`,
1931
2106
  metadata.quoteContext
1932
2107
  );
2108
+ validateOptionalNullableString(`${label}.quotePrefix`, metadata.quotePrefix);
2109
+ validateOptionalNullableString(`${label}.quoteSuffix`, metadata.quoteSuffix);
1933
2110
  validateOptionalTimestamp(`${label}.createdAt`, metadata.createdAt);
1934
2111
  validateOptionalTimestamp(`${label}.updatedAt`, metadata.updatedAt);
1935
2112
  return documentId;
@@ -1961,6 +2138,21 @@ function validateEquationReadMetadata(label, value) {
1961
2138
  validateOptionalTimestamp(`${label}.updatedAt`, metadata.updatedAt);
1962
2139
  return documentId;
1963
2140
  }
2141
+ function validateNoteMetadata(label, value, validateDocumentId) {
2142
+ const metadata = assertRecord(label, value);
2143
+ rejectUnexpectedKeys(label, metadata, NOTE_READ_METADATA_KEYS);
2144
+ const documentId = metadata.documentId === null ? void 0 : validateDocumentId(`${label}.documentId`, metadata.documentId);
2145
+ if (metadata.attachedTitle !== null && typeof metadata.attachedTitle !== "string") {
2146
+ throw new PapiersCliResponseValidationError(
2147
+ `${label}.attachedTitle was not a string or null.`
2148
+ );
2149
+ }
2150
+ assertTimestampString(`${label}.editedAt`, metadata.editedAt);
2151
+ assertTimestampString(`${label}.createdAt`, metadata.createdAt);
2152
+ assertTimestampString(`${label}.updatedAt`, metadata.updatedAt);
2153
+ assertNonEmptyString(`${label}.title`, metadata.title);
2154
+ return documentId;
2155
+ }
1964
2156
  function rejectUnexpectedContent(label, item) {
1965
2157
  if (item.content !== void 0 && item.content !== null) {
1966
2158
  throw new PapiersCliResponseValidationError(
@@ -2188,19 +2380,20 @@ function validateReadUsage(label, rawUsage, options) {
2188
2380
  function validateReadItemError(label, value, kind, expectedFormat) {
2189
2381
  const error = assertRecord(label, value);
2190
2382
  rejectUnexpectedKeys(label, error, READ_ITEM_ERROR_KEYS);
2191
- if (error.code !== DOCUMENT_READ_NOT_FOUND_CODE) {
2192
- if (kind !== "document" && kind !== "passage" || error.code !== DOCUMENT_READ_SOURCE_SUPERSEDED_CODE) {
2193
- throw new PapiersCliResponseValidationError(
2194
- `${label}.code was not a supported read error code.`
2195
- );
2196
- }
2383
+ const sourceSupersededAllowed = error.code === DOCUMENT_READ_SOURCE_SUPERSEDED_CODE && (kind === "document" || kind === "passage" || kind === "note" || kind === "conversation");
2384
+ const contentLimitAllowed = error.code === CONVERSATION_READ_CONTENT_LIMIT_CODE && kind === "conversation";
2385
+ const requestLimitAllowed = error.code === CONVERSATION_READ_REQUEST_LIMIT_CODE && kind === "conversation";
2386
+ if (error.code !== DOCUMENT_READ_NOT_FOUND_CODE && !sourceSupersededAllowed && !contentLimitAllowed && !requestLimitAllowed) {
2387
+ throw new PapiersCliResponseValidationError(
2388
+ `${label}.code was not a supported read error code.`
2389
+ );
2197
2390
  }
2198
- if (expectedFormat === "metadata" && error.code === DOCUMENT_READ_SOURCE_SUPERSEDED_CODE) {
2391
+ if (expectedFormat === "metadata" && (error.code === DOCUMENT_READ_SOURCE_SUPERSEDED_CODE || error.code === CONVERSATION_READ_CONTENT_LIMIT_CODE || error.code === CONVERSATION_READ_REQUEST_LIMIT_CODE)) {
2199
2392
  throw new PapiersCliResponseValidationError(
2200
2393
  `${label}.code was not supported for metadata reads.`
2201
2394
  );
2202
2395
  }
2203
- const expectedMessage = error.code === DOCUMENT_READ_NOT_FOUND_CODE ? readNotFoundMessageForKind(kind) : kind === "passage" ? PASSAGE_READ_CONTENT_UNAVAILABLE_MESSAGE : DOCUMENT_READ_SOURCE_SUPERSEDED_MESSAGE;
2396
+ const expectedMessage = error.code === DOCUMENT_READ_NOT_FOUND_CODE ? readNotFoundMessageForKind(kind) : error.code === CONVERSATION_READ_CONTENT_LIMIT_CODE ? CONVERSATION_READ_CONTENT_LIMIT_MESSAGE : error.code === CONVERSATION_READ_REQUEST_LIMIT_CODE ? CONVERSATION_READ_REQUEST_LIMIT_MESSAGE : kind === "conversation" ? CONVERSATION_READ_SOURCE_SUPERSEDED_MESSAGE : kind === "passage" ? PASSAGE_READ_CONTENT_UNAVAILABLE_MESSAGE : kind === "note" ? NOTE_READ_SOURCE_SUPERSEDED_MESSAGE : DOCUMENT_READ_SOURCE_SUPERSEDED_MESSAGE;
2204
2397
  if (error.message !== expectedMessage) {
2205
2398
  throw new PapiersCliResponseValidationError(
2206
2399
  `${label}.message was not a supported read error message.`
@@ -2225,6 +2418,8 @@ function readNotFoundMessageForKind(kind) {
2225
2418
  return CONVERSATION_READ_NOT_FOUND_MESSAGE;
2226
2419
  case "equation":
2227
2420
  return EQUATION_READ_NOT_FOUND_MESSAGE;
2421
+ case "note":
2422
+ return NOTE_READ_NOT_FOUND_MESSAGE;
2228
2423
  }
2229
2424
  }
2230
2425
  function validateOptionalSearchQuality(label, value) {
@@ -2289,7 +2484,7 @@ function validateOptionalReadWarnings(label, value, format, kind) {
2289
2484
  );
2290
2485
  }
2291
2486
  hasReadTruncatedWarning = true;
2292
- const expectedMessage = kind === "document" ? DOCUMENT_READ_TRUNCATED_WARNING_MESSAGE : PASSAGE_READ_TRUNCATED_WARNING_MESSAGE;
2487
+ const expectedMessage = kind === "document" ? DOCUMENT_READ_TRUNCATED_WARNING_MESSAGE : kind === "passage" ? PASSAGE_READ_TRUNCATED_WARNING_MESSAGE : NOTE_READ_TRUNCATED_WARNING_MESSAGE;
2293
2488
  if (warning.message !== expectedMessage) {
2294
2489
  throw new PapiersCliResponseValidationError(
2295
2490
  `${label} ${index}.message was not a supported warning message.`
@@ -2709,10 +2904,18 @@ function assertDocId(label, value) {
2709
2904
  }
2710
2905
  return value;
2711
2906
  }
2712
- function assertReadPublicId(label, value) {
2713
- if (typeof value !== "string" || publicReadKindForId(value) === null) {
2907
+ function assertDocumentReadIdentifier(label, value) {
2908
+ if (typeof value !== "string" || !isDocumentReadIdentifier(value)) {
2714
2909
  throw new PapiersCliResponseValidationError(
2715
- `${label} was not a supported public read id.`
2910
+ `${label} was not a doc_* public id or papiers://item locator.`
2911
+ );
2912
+ }
2913
+ return value;
2914
+ }
2915
+ function assertReadIdentifier(label, value) {
2916
+ if (typeof value !== "string" || readKindForIdentifier(value) === null) {
2917
+ throw new PapiersCliResponseValidationError(
2918
+ `${label} was not a supported read identifier.`
2716
2919
  );
2717
2920
  }
2718
2921
  return value;
@@ -2856,7 +3059,7 @@ async function readDocuments(config, fetchImpl, request) {
2856
3059
  "/api/v1/papiers/read",
2857
3060
  { method: "POST", body: request, retryable: true }
2858
3061
  );
2859
- const readKind = publicReadKindForId(request.ids[0] ?? "");
3062
+ const readKind = readKindForIdentifier(request.ids[0] ?? "");
2860
3063
  assertPapiersReadResponse("read", body, request.ids, {
2861
3064
  // Every kind resolves an omitted format server-side (auto semantics),
2862
3065
  // so there is nothing to echo-check unless the caller pinned one;
@@ -2903,7 +3106,7 @@ function expectedKindForListSearchRequest(request) {
2903
3106
  function expectedKindsForSearchRequest(request) {
2904
3107
  const kinds = request.kinds;
2905
3108
  if (kinds === void 0) {
2906
- return { expectedKinds: /* @__PURE__ */ new Set(["document", "conversation"]) };
3109
+ return { expectedKinds: /* @__PURE__ */ new Set(["document", "conversation", "note"]) };
2907
3110
  }
2908
3111
  if (!Array.isArray(kinds) || kinds.length === 0) {
2909
3112
  throw new CliUsageError(
@@ -2923,11 +3126,14 @@ function expectedKindsForSearchRequest(request) {
2923
3126
  );
2924
3127
  }
2925
3128
  }
2926
- const normalizedKinds = new Set(rawKinds);
2927
- if (normalizedKinds.size === 1 && normalizedKinds.has("conversation")) {
2928
- return { expectedKind: "conversation" };
3129
+ const expectedKinds = /* @__PURE__ */ new Set();
3130
+ for (const kind of rawKinds) {
3131
+ expectedKinds.add(
3132
+ kind === "conversation" || kind === "note" ? kind : "document"
3133
+ );
2929
3134
  }
2930
- return normalizedKinds.has("conversation") ? { expectedKinds: /* @__PURE__ */ new Set(["document", "conversation"]) } : { expectedKind: "document" };
3135
+ const [onlyKind] = expectedKinds;
3136
+ return expectedKinds.size === 1 ? { expectedKind: onlyKind } : { expectedKinds };
2931
3137
  }
2932
3138
  async function requestPapiersApi(config, fetchImpl, path, init) {
2933
3139
  if (!config.token) {
@@ -3598,16 +3804,18 @@ var FORMER_KIND_ALIASES = /* @__PURE__ */ new Map([
3598
3804
  ["chat", "conversation"],
3599
3805
  ["chats", "conversation"],
3600
3806
  ["conversations", "conversation"],
3601
- ["equations", "equation"]
3807
+ ["equations", "equation"],
3808
+ ["notes", "note"]
3602
3809
  ]);
3603
3810
  var MULTI_SEARCH_KINDS = [
3604
3811
  "passage",
3605
3812
  "highlight",
3606
3813
  "conversation",
3607
- "equation"
3814
+ "equation",
3815
+ "note"
3608
3816
  ];
3609
3817
  var GLOBAL_HELP = `Papiers CLI: search and read your library from the terminal.
3610
- Your saved papers, highlights, and conversations (your chats) \u2014 for you or an AI agent.
3818
+ Your saved papers, highlights, notes, and conversations (your chats) \u2014 for you or an AI agent.
3611
3819
 
3612
3820
  GETTING STARTED
3613
3821
  papiers auth login Sign in with a token from Papiers (Settings -> API tokens)
@@ -3616,8 +3824,8 @@ GETTING STARTED
3616
3824
 
3617
3825
  COMMANDS
3618
3826
  search Search across your library
3619
- list List your documents, highlights, and conversations
3620
- read Print a document, highlight, or conversation
3827
+ list List your documents, highlights, notes, and conversations
3828
+ read Print a document, highlight, note, or conversation
3621
3829
  browse Page through an earlier search's results
3622
3830
  auth Sign in, check status, or sign out
3623
3831
  tokens List or revoke your API tokens
@@ -3654,8 +3862,8 @@ USAGE
3654
3862
 
3655
3863
  OPTIONS
3656
3864
  --kind KIND what to search. Defaults to all \u2014 your passages, highlights,
3657
- equations, conversations, and matching titles. Or focus on one:
3658
- passage \xB7 highlight \xB7 conversation \xB7 equation.
3865
+ equations, notes, conversations, and matching titles. Or focus on one:
3866
+ passage \xB7 highlight \xB7 conversation \xB7 equation \xB7 note.
3659
3867
  --filter FIELD.OP=VALUE narrow parent documents \u2014 repeatable, up to 25.
3660
3868
  e.g. --filter publishedYear.gte=2020 --filter author.contains=hinton. Fields/ops: help --all.
3661
3869
  --document-id ID scope to documents: doc_\u2026 (comma-separated or repeated), or @-/@file.
@@ -3670,7 +3878,7 @@ EXAMPLES
3670
3878
  papiers search retrieval --kind passage --limit 5 --passage-ids | papiers read @- --format plain --json
3671
3879
 
3672
3880
  When capabilities.searchResultsBrowse.enabled is true, a search may return ranking.searchId \u2014 pass it to \`papiers browse\` for more pages.`,
3673
- list: `papiers list \u2014 list your documents, highlights, and conversations.
3881
+ list: `papiers list \u2014 list your documents, highlights, notes, and conversations.
3674
3882
 
3675
3883
  USAGE
3676
3884
  papiers list [--kind KIND] [--limit N] [--cursor CURSOR] [--pretty|--json|--ids|--ids0|--doc-ids]
@@ -3678,7 +3886,7 @@ USAGE
3678
3886
  papiers list --kind highlight|conversation [--since WHEN]
3679
3887
 
3680
3888
  OPTIONS
3681
- --kind KIND document (default) \xB7 passage \xB7 highlight \xB7 conversation \xB7 equation.
3889
+ --kind KIND document (default) \xB7 passage \xB7 highlight \xB7 conversation \xB7 equation \xB7 note.
3682
3890
  --filter FIELD.OP=VALUE narrow document results \u2014 repeatable, up to 25. document kind only.
3683
3891
  e.g. --filter lastEngagedAt.gte=30d --filter annotated=true. Fields/ops: help --all.
3684
3892
  --order-by KEY addedAt:desc (default) or lastEngagedAt:desc. document kind only.
@@ -3695,29 +3903,32 @@ EXAMPLES
3695
3903
  papiers list --kind highlight --since 30d --pretty
3696
3904
  papiers list --kind document --filter lastEngagedAt.gte=30d --json
3697
3905
  papiers list --kind document --filter annotated=true --json`,
3698
- read: `papiers read \u2014 print documents, highlights, conversations, passages, or equations.
3906
+ read: `papiers read \u2014 print documents, highlights, notes, conversations, passages, or equations.
3699
3907
 
3700
3908
  USAGE
3701
3909
  papiers read ID [ID ...] [--format FORMAT] [--max-chars-per-item N] [--fail-fast] [--json|--pretty]
3702
3910
  papiers read @- | @file [--input0] [--format FORMAT] [--max-chars-per-item N] [--json|--pretty]
3703
- Each ID is doc_\u2026 / pas_\u2026 / hl_\u2026 / cnv_\u2026 / eq_\u2026 \u2014 the kind is read from the prefix; don't mix kinds.
3911
+ Read public IDs or papiers://note|highlight|item|conversation/<uuid> locators; don't mix resource kinds.
3704
3912
 
3705
3913
  OPTIONS
3706
3914
  --format FORMAT how to render each item. Default auto (equation defaults to metadata).
3707
3915
  document: auto|metadata|markdown|plain. passage: markdown|plain.
3708
- highlight, conversation: metadata|plain. equation: metadata.
3916
+ highlight, conversation: metadata|plain. note: markdown|plain.
3917
+ equation: metadata.
3709
3918
  --include SECTION extra sections, comma-separated: metadata, source, locations.
3710
- document only (highlight/conversation/equation: metadata only; passage: none).
3919
+ document only (highlight/conversation/equation/note: metadata only; passage: none).
3711
3920
  --pages RANGE restrict a document read to pages, e.g. 1,3-5. document only.
3712
3921
  --max-chars-per-item N truncate each item's content to N chars, 1\u2013200000.
3713
- --cursor CURSOR continue one long document \u2014 use items[].nextCursor from the previous
3714
- response. One literal doc_\u2026 only, with --format auto, markdown, or plain.
3922
+ --cursor CURSOR continue one long document or note \u2014 use items[].nextCursor from the
3923
+ previous response. One literal document or note identifier only, with
3924
+ --format auto, markdown, or plain.
3715
3925
  --fail-fast exit non-zero if any item fails (default: succeed if at least one does).
3716
3926
  --json machine-readable JSON (the default)
3717
3927
  --pretty human-readable text
3718
3928
 
3719
3929
  EXAMPLES
3720
3930
  papiers read doc_<id> --format markdown --max-chars-per-item 2000 --json
3931
+ papiers read nte_<id> --format markdown --json
3721
3932
  papiers read hl_<id> --json
3722
3933
  papiers search retrieval --doc-ids | papiers read @- --format metadata --json`,
3723
3934
  browse: `papiers browse \u2014 page deeper into an earlier search's results.
@@ -3771,21 +3982,27 @@ var REFERENCE = `REFERENCE \u2014 IDs, piping, pagination, and limits.
3771
3982
 
3772
3983
  ID PREFIXES
3773
3984
  doc_ document pas_ passage hl_ highlight
3774
- cnv_ conversation eq_ equation srch_ a saved search ranking (for browse)
3985
+ cnv_ conversation eq_ equation nte_ note
3986
+ srch_ a saved search ranking (for browse)
3775
3987
  On read, the kind \u2014 and which --format/--include apply \u2014 is read from the prefix.
3776
3988
 
3989
+ RESOURCE LOCATORS
3990
+ papiers://note/<uuid> papiers://highlight/<uuid>
3991
+ papiers://item/<uuid> papiers://conversation/<uuid>
3992
+ Locators are accepted by read and resolved with the same ownership checks as public IDs.
3993
+
3777
3994
  PIPING IDS BETWEEN COMMANDS
3778
3995
  --ids result IDs, one per line, with their native prefix
3779
3996
  --ids0 the same IDs, NUL-separated (for \`xargs -0\`)
3780
3997
  --doc-ids parent document IDs only (doc_\u2026), deduped; conversation hits are skipped
3781
- @- read IDs from stdin @file read IDs from a file
3782
- --input0 read piped IDs NUL-separated instead of whitespace/newlines
3783
- @- and @file accept plain IDs or a Papiers JSON envelope, and may be mixed with literal IDs.
3998
+ @- read identifiers from stdin @file read identifiers from a file
3999
+ --input0 read piped identifiers NUL-separated instead of whitespace/newlines
4000
+ @- and @file accept IDs, resource locators, or a Papiers JSON envelope, and may be mixed with literals.
3784
4001
 
3785
4002
  PAGINATION \u2014 three separate mechanisms
3786
4003
  list response has page.nextCursor \u2192 papiers list \u2026 --cursor CURSOR
3787
4004
  search response has ranking.searchId \u2192 papiers browse srch_\u2026 --offset N
3788
- read a truncated item has items[].nextCursor \u2192 papiers read doc_\u2026 --cursor CURSOR
4005
+ read a truncated item has items[].nextCursor \u2192 papiers read NOTE_OR_DOCUMENT --cursor CURSOR
3789
4006
 
3790
4007
  LIMITS
3791
4008
  --limit 1\u201350 \xB7 --max-chars-per-item 1\u2013200000 \xB7 up to 25 filters \xB7 up to 100 IDs per request
@@ -4085,7 +4302,7 @@ function buildReadRequest(parsed) {
4085
4302
  const ids = orderedReadIdValues(parsed);
4086
4303
  if (ids.length === 0) {
4087
4304
  throw new CliUsageError(
4088
- "papiers read requires at least one doc_*, pas_*, hl_*, cnv_*, or eq_* ID."
4305
+ "papiers read requires at least one public ID or papiers:// resource locator."
4089
4306
  );
4090
4307
  }
4091
4308
  if (ids.filter((id) => id === "@-").length > 1) {
@@ -4099,7 +4316,7 @@ function buildReadRequest(parsed) {
4099
4316
  const literalKinds = readKindsForLiteralIds(ids);
4100
4317
  if (literalKinds.length > 1) {
4101
4318
  throw new CliUsageError(
4102
- "papiers read IDs must all use the same public resource kind."
4319
+ "papiers read identifiers must all use the same resource kind."
4103
4320
  );
4104
4321
  }
4105
4322
  const inferredKind = literalKinds[0] ?? requestedKind ?? "document";
@@ -4115,9 +4332,9 @@ function buildReadRequest(parsed) {
4115
4332
  }
4116
4333
  const format = parseReadFormat(parsed, inferredKind);
4117
4334
  const cursor = singleFlagValue(parsed, "cursor");
4118
- if (cursor !== void 0 && (inferredKind !== "document" || ids.length !== 1 || ids.some((id) => isDocumentIdSource(id)) || !isDocumentPublicId(ids[0]))) {
4335
+ if (cursor !== void 0 && (inferredKind !== "document" && inferredKind !== "note" || ids.length !== 1 || ids.some((id) => isDocumentIdSource(id)) || readKindForIdentifier(ids[0]) !== inferredKind)) {
4119
4336
  throw new CliUsageError(
4120
- "papiers read --cursor requires exactly one literal doc_* ID."
4337
+ "papiers read --cursor requires exactly one literal document or note identifier."
4121
4338
  );
4122
4339
  }
4123
4340
  if (cursor !== void 0 && format !== "auto" && !DOCUMENT_CONTENT_READ_FORMATS.some((supported) => supported === format)) {
@@ -4237,7 +4454,7 @@ function parseSearchKindScopes(parsed) {
4237
4454
  const kind = normalizeCliPublicKind(rawKind);
4238
4455
  if (!kind) {
4239
4456
  throw new CliUsageError(
4240
- `papiers search supports --kind passage, highlight, conversation, or equation; received "${rawKind}".${didYouMeanKind(rawKind)}`
4457
+ `papiers search supports --kind passage, highlight, conversation, equation, or note; received "${rawKind}".${didYouMeanKind(rawKind)}`
4241
4458
  );
4242
4459
  }
4243
4460
  if (kind === "document") {
@@ -4247,7 +4464,7 @@ function parseSearchKindScopes(parsed) {
4247
4464
  }
4248
4465
  if (!MULTI_SEARCH_KINDS.includes(kind)) {
4249
4466
  throw new CliUsageError(
4250
- `papiers search supports --kind passage, highlight, conversation, or equation; received "${rawKind}".`
4467
+ `papiers search supports --kind passage, highlight, conversation, equation, or note; received "${rawKind}".`
4251
4468
  );
4252
4469
  }
4253
4470
  kinds.push(kind);
@@ -4327,7 +4544,7 @@ function parseReadKind(parsed) {
4327
4544
  return normalized;
4328
4545
  }
4329
4546
  throw new CliUsageError(
4330
- `Public CLI read currently supports --kind document, passage, highlight, conversation, or equation only; received "${kind}".${didYouMeanKind(kind)}`
4547
+ `Public CLI read currently supports --kind document, passage, highlight, conversation, equation, or note only; received "${kind}".${didYouMeanKind(kind)}`
4331
4548
  );
4332
4549
  }
4333
4550
  function readKindsForLiteralIds(ids) {
@@ -4336,10 +4553,10 @@ function readKindsForLiteralIds(ids) {
4336
4553
  if (isDocumentIdSource(id)) {
4337
4554
  continue;
4338
4555
  }
4339
- const kind = publicReadKindForId(id);
4556
+ const kind = readKindForIdentifier(id);
4340
4557
  if (!kind) {
4341
4558
  throw new CliUsageError(
4342
- "papiers read currently accepts only doc_*, pas_*, hl_*, cnv_*, or eq_* IDs."
4559
+ "papiers read accepts public IDs or papiers://note, highlight, item, and conversation locators."
4343
4560
  );
4344
4561
  }
4345
4562
  kinds.add(kind);
@@ -4447,6 +4664,21 @@ function validateReadOptionsForKind(parsed, kind, format) {
4447
4664
  });
4448
4665
  return;
4449
4666
  }
4667
+ if (kind === "note") {
4668
+ if (format !== "auto" && !EXPECTED_NOTE_READ_FORMATS.some((supported) => supported === format)) {
4669
+ throw new CliUsageError(
4670
+ "Public note read supports --format markdown, plain, or auto."
4671
+ );
4672
+ }
4673
+ const includes = parseReadInclude(parsed);
4674
+ if (includes?.some((include) => include !== "metadata")) {
4675
+ throw new CliUsageError(
4676
+ "Public note read supports only --include metadata."
4677
+ );
4678
+ }
4679
+ rejectReadFlagsForKind(parsed, kind, ["pages"]);
4680
+ return;
4681
+ }
4450
4682
  validateMetadataReadOptionsForKind(parsed, kind, format, {
4451
4683
  supportedFormats: EXPECTED_EQUATION_READ_FORMATS
4452
4684
  });
@@ -4614,7 +4846,7 @@ function parseListSearchKind(parsed, command) {
4614
4846
  );
4615
4847
  }
4616
4848
  throw new CliUsageError(
4617
- `papiers ${command} supports --kind document, passage, highlight, conversation, or equation; received "${kind}".${didYouMeanKind(kind)}`
4849
+ `papiers ${command} supports --kind document, passage, highlight, conversation, equation, or note; received "${kind}".${didYouMeanKind(kind)}`
4618
4850
  );
4619
4851
  }
4620
4852
  function normalizeCliPublicKind(value) {
@@ -4921,6 +5153,13 @@ function assertSupportedDocumentCapabilities(value) {
4921
5153
  EXPECTED_EQUATION_READ_FORMATS
4922
5154
  );
4923
5155
  continue;
5156
+ case "note":
5157
+ assertOptionalReadFormats(
5158
+ `capabilities readFormatsByKind.${kind}`,
5159
+ actualFormats,
5160
+ EXPECTED_NOTE_READ_FORMATS
5161
+ );
5162
+ continue;
4924
5163
  default:
4925
5164
  assertSameStringSet(
4926
5165
  `capabilities readFormatsByKind.${kind}`,
@@ -5661,16 +5900,13 @@ async function runAuthLogin(params) {
5661
5900
  }
5662
5901
  assertSafeBearerApiBaseUrl(params.config.apiBaseUrl);
5663
5902
  const token = await loginWithPatStdin(params);
5664
- const validationConfig = cliConfigWithToken(
5665
- params.config,
5666
- token,
5667
- "stdin"
5903
+ const validationConfig = cliConfigWithToken(params.config, token, "stdin");
5904
+ const capabilities = await getCapabilities(
5905
+ validationConfig,
5906
+ params.fetchImpl
5668
5907
  );
5669
- let capabilities;
5670
- capabilities = await getCapabilities(validationConfig, params.fetchImpl);
5671
5908
  assertCliSupportedCapabilities(capabilities);
5672
- let update;
5673
- update = writeLocalAuthToken({
5909
+ const update = writeLocalAuthToken({
5674
5910
  root: params.envRoot,
5675
5911
  token,
5676
5912
  apiBaseUrl: params.config.apiBaseUrl
@@ -6085,7 +6321,8 @@ function writeParentDocumentIds(stdout, items) {
6085
6321
  const seen = /* @__PURE__ */ new Set();
6086
6322
  const ids = [];
6087
6323
  for (const item of items) {
6088
- const docId = item.kind === "document" ? item.id : item.documentId;
6324
+ const metadataDocumentId = item.kind === "note" && typeof item.metadata === "object" && item.metadata !== null && !Array.isArray(item.metadata) ? item.metadata.documentId : void 0;
6325
+ const docId = item.kind === "document" ? item.id : item.documentId ?? metadataDocumentId;
6089
6326
  if (typeof docId !== "string" || !docId) {
6090
6327
  continue;
6091
6328
  }
@@ -6348,23 +6585,23 @@ function validateReadIds(ids, context, sourceLabel) {
6348
6585
  }
6349
6586
  const kinds = /* @__PURE__ */ new Set();
6350
6587
  for (const [index, id] of ids.entries()) {
6351
- const kind = publicReadKindForId(id);
6588
+ const kind = readKindForIdentifier(id);
6352
6589
  if (!kind) {
6353
6590
  throw new CliUsageError(
6354
- `${context} ${sourceLabel} item ${index + 1} was not a doc_*, pas_*, hl_*, cnv_*, or eq_* ID.`
6591
+ `${context} ${sourceLabel} item ${index + 1} was not a public ID or papiers:// resource locator.`
6355
6592
  );
6356
6593
  }
6357
6594
  kinds.add(kind);
6358
6595
  }
6359
6596
  if (kinds.size > 1) {
6360
6597
  throw new CliUsageError(
6361
- `${context} ${sourceLabel} IDs must all use the same public resource kind.`
6598
+ `${context} ${sourceLabel} identifiers must all use the same resource kind.`
6362
6599
  );
6363
6600
  }
6364
6601
  return ids;
6365
6602
  }
6366
6603
  function validateExpandedReadRequest(request, ids, requestedReadKind) {
6367
- const kind = publicReadKindForId(ids[0] ?? "");
6604
+ const kind = readKindForIdentifier(ids[0] ?? "");
6368
6605
  if (requestedReadKind && kind && requestedReadKind !== kind) {
6369
6606
  throw new CliUsageError(
6370
6607
  `papiers read --kind ${requestedReadKind} does not match ${kind} IDs.`
@@ -6395,6 +6632,25 @@ function validateExpandedReadRequest(request, ids, requestedReadKind) {
6395
6632
  }
6396
6633
  if (kind === "equation") {
6397
6634
  validateMetadataOnlyReadRequest(request, kind);
6635
+ return;
6636
+ }
6637
+ if (kind === "note") {
6638
+ validateNoteReadRequest(request);
6639
+ }
6640
+ }
6641
+ function validateNoteReadRequest(request) {
6642
+ if (request.format !== void 0 && request.format !== "auto" && request.format !== "markdown" && request.format !== "plain") {
6643
+ throw new CliUsageError(
6644
+ "Public note read supports --format markdown, plain, or auto."
6645
+ );
6646
+ }
6647
+ if (request.include?.some((include) => include !== "metadata")) {
6648
+ throw new CliUsageError(
6649
+ "Public note read supports only --include metadata."
6650
+ );
6651
+ }
6652
+ if (request.scope?.pages || request.scope?.sectionId) {
6653
+ throw new CliUsageError("Public note read does not support --pages.");
6398
6654
  }
6399
6655
  }
6400
6656
  function validateContentCapableReadRequest(request, kind) {
@@ -6501,10 +6757,10 @@ function parseJsonEnvelopeIds(input, context, sourceLabel) {
6501
6757
  );
6502
6758
  }
6503
6759
  const kind = item.kind;
6504
- const idKind = publicReadKindForId(id);
6760
+ const idKind = envelope.schemaVersion === PAPIERS_SCHEMA_VERSIONS.read ? readKindForIdentifier(id) : publicReadKindForId(id);
6505
6761
  if (kind !== idKind) {
6506
6762
  throw new CliUsageError(
6507
- `${context} JSON item ${index} kind did not match its public id.`
6763
+ `${context} JSON item ${index} kind did not match its identifier.`
6508
6764
  );
6509
6765
  }
6510
6766
  return id;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "papiers",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Read-only CLI for your Papiers library.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://papiers.ai",
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: papiers-research
3
- version: 1.2.0
3
+ version: 1.2.1
4
4
  description: |
5
5
  Ground proposals, experiment designs, reviews, and writing in the user's
6
- Papiers library — their highlights, annotations, conversations, and
6
+ Papiers library — their highlights, notes, annotations, conversations, and
7
7
  curated-but-unread saves — before generating. Use whenever output should
8
8
  be shaped by what the user has read and thought, not generic knowledge:
9
9
  proposing research ideas, designing experiments, advising on decisions,
@@ -17,7 +17,7 @@ allowed-tools:
17
17
  The `papiers` CLI is the agent-readable memory of what the user has read and
18
18
  thought. It holds two kinds of signal, and you must treat them differently:
19
19
 
20
- - **Judgement** (read and marked): highlights, annotations, and past
20
+ - **Judgement** (read and marked): highlights, notes, annotations, and past
21
21
  conversations. These are the user's own words — stances, open questions,
22
22
  connections. The strongest signal you have.
23
23
  - **Curation** (saved, possibly unread): library membership itself. The user
@@ -36,13 +36,14 @@ once for the full contract. `PAPIERS_TOKEN` must be configured.
36
36
 
37
37
  ```bash
38
38
  papiers list --kind highlight --since 30d --limit 20 --json
39
+ papiers list --kind note --limit 20 --json
39
40
  papiers list --kind document --limit 20 --json # scan recent titles
40
41
  ```
41
42
 
42
43
  2. **Facet sweep.** Decompose the task into 3–6 facets (for "improve MoE
43
44
  router load balancing": expert routing, load balancing loss, expert
44
45
  collapse, capacity factor…). One default search covers matching titles,
45
- passages, highlights, equations, and conversations in a single fused
46
+ passages, highlights, equations, notes, and conversations in a single fused
46
47
  ranking:
47
48
 
48
49
  ```bash
@@ -56,9 +57,10 @@ once for the full contract. `PAPIERS_TOKEN` must be configured.
56
57
  for an author surname or `author.contains=karpathy` for a handle.
57
58
 
58
59
  Document results carry nested `evidence[]` for passage, highlight, and
59
- equation hits. Use `--passage-ids`, `--highlight-ids`, or `--evidence-ids`
60
- to project matching evidence flat for piping. `--doc-ids` pivots any
61
- result page to deduped parent documents, skipping conversation hits.
60
+ equation hits; notes and conversations are standalone results. Use
61
+ `--passage-ids`, `--highlight-ids`, or `--evidence-ids` to project matching
62
+ evidence flat for piping. `--doc-ids` pivots any result page to deduped
63
+ parent documents, including attached notes and skipping conversations.
62
64
 
63
65
  3. **Delegated reading.** Document hits you cannot tie to any judgement are
64
66
  curated-but-unread candidates (`--filter engaged=false` lists the queue).
@@ -68,6 +70,7 @@ once for the full contract. `PAPIERS_TOKEN` must be configured.
68
70
 
69
71
  ```bash
70
72
  papiers read doc_<id> --max-chars-per-item 4000 --json
73
+ papiers read nte_<id> --max-chars-per-item 4000 --json
71
74
  papiers search "<topic>" --doc-ids | papiers read @- --format metadata --json
72
75
  papiers search "<subtopic>" --kind passage --document-id doc_<id> --json # search within one document
73
76
  ```
@@ -108,7 +111,7 @@ once for the full contract. `PAPIERS_TOKEN` must be configured.
108
111
  ISO timestamps or `30d`-style shorthand.
109
112
  - **Honor the quality signal.** `quality.signal: "empty"` or `"low"` means
110
113
  the library has nothing — report that rather than citing weak matches.
111
- - **Cite IDs.** `hl_/cnv_/doc_/pas_` IDs are stable; include them so the
114
+ - **Cite IDs.** `hl_/cnv_/doc_/pas_/eq_/nte_` IDs are stable; include them so the
112
115
  user can jump to sources.
113
116
  - **Budgets are policy, not protocol.** Default to ~10–20 queries and 3–8
114
117
  document reads per task. The user may override any policy in this skill