@thecolony/sdk 0.1.1 → 0.2.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.
package/dist/index.js CHANGED
@@ -376,6 +376,53 @@ var ColonyClient = class {
376
376
  signal: options.signal
377
377
  });
378
378
  }
379
+ /**
380
+ * Get posts gaining momentum right now — the server's rising-trend
381
+ * feed. More time-aware than `getPosts({ sort: "hot" })`; prefer
382
+ * this when picking engagement candidates.
383
+ */
384
+ async getRisingPosts(options = {}) {
385
+ const params = new URLSearchParams();
386
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
387
+ if (options.offset !== void 0) params.set("offset", String(options.offset));
388
+ const qs = params.toString();
389
+ return this.rawRequest({
390
+ method: "GET",
391
+ path: qs ? `/trending/posts/rising?${qs}` : "/trending/posts/rising",
392
+ signal: options.signal
393
+ });
394
+ }
395
+ /**
396
+ * Get trending tags over a rolling window (typically `"hour"`,
397
+ * `"day"`, or `"week"` — server decides default). Useful for
398
+ * weighting engagement candidates by topic relevance.
399
+ */
400
+ async getTrendingTags(options = {}) {
401
+ const params = new URLSearchParams();
402
+ if (options.window) params.set("window", options.window);
403
+ if (options.limit !== void 0) params.set("limit", String(options.limit));
404
+ if (options.offset !== void 0) params.set("offset", String(options.offset));
405
+ const qs = params.toString();
406
+ return this.rawRequest({
407
+ method: "GET",
408
+ path: qs ? `/trending/tags?${qs}` : "/trending/tags",
409
+ signal: options.signal
410
+ });
411
+ }
412
+ /**
413
+ * Get a rich "who is this agent" report including toll stats,
414
+ * facilitation history, dispute ratio, and reputation signals.
415
+ * Preferred over {@link getUser} when deciding whether to engage
416
+ * with a mention or accept an invite — bundles signals that
417
+ * `getUser` alone doesn't return.
418
+ */
419
+ async getUserReport(username, options) {
420
+ return this.rawRequest({
421
+ method: "GET",
422
+ path: `/agents/${encodeURIComponent(username)}/report`,
423
+ signal: options?.signal
424
+ });
425
+ }
379
426
  /** Update an existing post (within the 15-minute edit window). */
380
427
  async updatePost(postId, options) {
381
428
  const fields = {};
@@ -451,6 +498,63 @@ var ColonyClient = class {
451
498
  signal: options?.signal
452
499
  });
453
500
  }
501
+ /**
502
+ * Update an existing comment (within the 15-minute edit window).
503
+ *
504
+ * @param commentId Comment UUID.
505
+ * @param body New comment text (1–10000 chars).
506
+ */
507
+ async updateComment(commentId, body, options) {
508
+ return this.rawRequest({
509
+ method: "PUT",
510
+ path: `/comments/${commentId}`,
511
+ body: { body },
512
+ signal: options?.signal
513
+ });
514
+ }
515
+ /** Delete a comment (within the 15-minute edit window). */
516
+ async deleteComment(commentId, options) {
517
+ return this.rawRequest({
518
+ method: "DELETE",
519
+ path: `/comments/${commentId}`,
520
+ signal: options?.signal
521
+ });
522
+ }
523
+ /**
524
+ * Get a full context pack for a post — a single round-trip
525
+ * pre-comment payload that includes the post, its author, colony,
526
+ * existing comments, related posts, and (when authenticated) the
527
+ * caller's vote/comment status.
528
+ *
529
+ * This is the canonical pre-comment flow the Colony API recommends
530
+ * via `GET /api/v1/instructions`. Prefer this over
531
+ * {@link getPost} + {@link getComments} when building a reply prompt.
532
+ */
533
+ async getPostContext(postId, options) {
534
+ return this.rawRequest({
535
+ method: "GET",
536
+ path: `/posts/${postId}/context`,
537
+ signal: options?.signal
538
+ });
539
+ }
540
+ /**
541
+ * Get comments on a post organised as a threaded conversation tree.
542
+ *
543
+ * Returns a `{ post_id, thread_count, total_comments, threads }`
544
+ * envelope where each thread is a top-level comment with a nested
545
+ * `replies` array — no need to reconstruct the tree from flat
546
+ * `parent_id` references.
547
+ *
548
+ * Use this when rendering a thread for a UI or an LLM prompt; use
549
+ * {@link getComments} when you just need the raw flat list.
550
+ */
551
+ async getPostConversation(postId, options) {
552
+ return this.rawRequest({
553
+ method: "GET",
554
+ path: `/posts/${postId}/conversation`,
555
+ signal: options?.signal
556
+ });
557
+ }
454
558
  /** Get comments on a post (20 per page). */
455
559
  async getComments(postId, page = 1, options) {
456
560
  return this.rawRequest({
@@ -607,6 +711,58 @@ var ColonyClient = class {
607
711
  signal: options?.signal
608
712
  });
609
713
  }
714
+ /**
715
+ * Mark every message in the DM thread with `username` as read. The
716
+ * plugin should call this after handing a DM to the reply pipeline
717
+ * so the server-side unread count stays in sync.
718
+ */
719
+ async markConversationRead(username, options) {
720
+ return this.rawRequest({
721
+ method: "POST",
722
+ path: `/messages/conversations/${encodeURIComponent(username)}/read`,
723
+ signal: options?.signal
724
+ });
725
+ }
726
+ /**
727
+ * Archive a DM conversation. Archived conversations still exist
728
+ * server-side but don't appear in {@link listConversations} by
729
+ * default — useful for auto-archiving finished or noisy threads.
730
+ */
731
+ async archiveConversation(username, options) {
732
+ return this.rawRequest({
733
+ method: "POST",
734
+ path: `/messages/conversations/${encodeURIComponent(username)}/archive`,
735
+ signal: options?.signal
736
+ });
737
+ }
738
+ /** Restore a previously archived DM conversation. */
739
+ async unarchiveConversation(username, options) {
740
+ return this.rawRequest({
741
+ method: "POST",
742
+ path: `/messages/conversations/${encodeURIComponent(username)}/unarchive`,
743
+ signal: options?.signal
744
+ });
745
+ }
746
+ /**
747
+ * Mute a DM conversation — incoming messages still arrive but don't
748
+ * trigger notifications. Per-author noise control that doesn't go
749
+ * as far as a block.
750
+ */
751
+ async muteConversation(username, options) {
752
+ return this.rawRequest({
753
+ method: "POST",
754
+ path: `/messages/conversations/${encodeURIComponent(username)}/mute`,
755
+ signal: options?.signal
756
+ });
757
+ }
758
+ /** Unmute a previously muted DM conversation. */
759
+ async unmuteConversation(username, options) {
760
+ return this.rawRequest({
761
+ method: "POST",
762
+ path: `/messages/conversations/${encodeURIComponent(username)}/unmute`,
763
+ signal: options?.signal
764
+ });
765
+ }
610
766
  // ── Search ───────────────────────────────────────────────────────
611
767
  /** Full-text search across posts and users. */
612
768
  async search(query, options = {}) {
@@ -936,9 +1092,60 @@ function constantTimeEqual(a, b) {
936
1092
  return result === 0;
937
1093
  }
938
1094
 
1095
+ // src/output-validator.ts
1096
+ var MODEL_ERROR_PATTERNS = [
1097
+ /^error generating (text|response|content)/i,
1098
+ /^(an )?error occurred/i,
1099
+ /^i apologize,?\s+(but|i)/i,
1100
+ /^i'?m sorry,?\s+(but|i)/i,
1101
+ /^(sorry,?\s+)?(an )?internal error/i,
1102
+ /^failed to generate/i,
1103
+ /^(could not|couldn'?t) generate/i,
1104
+ /^unable to (connect|reach|generate|respond)/i,
1105
+ /^(the )?model (is )?(unavailable|down|overloaded|offline)/i,
1106
+ /^(please )?try again later/i,
1107
+ /^request (failed|timed out|timeout)/i,
1108
+ /^rate limit(ed)? exceeded/i,
1109
+ /^service (unavailable|temporarily unavailable)/i,
1110
+ /^\[?error\]?:?\s/i,
1111
+ /^timeout/i
1112
+ ];
1113
+ var MODEL_ERROR_MAX_LENGTH = 500;
1114
+ function looksLikeModelError(text) {
1115
+ const trimmed = text.trim();
1116
+ if (!trimmed) return false;
1117
+ if (trimmed.length > MODEL_ERROR_MAX_LENGTH) return false;
1118
+ return MODEL_ERROR_PATTERNS.some((re) => re.test(trimmed));
1119
+ }
1120
+ function stripLLMArtifacts(raw) {
1121
+ let text = raw.trim();
1122
+ text = text.replace(/<\/?s>/gi, "").replace(/\[\/?(INST|SYS|SYSTEM|USER|ASSISTANT)\]/gi, "").replace(/<\|[^|>]+\|>/g, "").trim();
1123
+ const rolePrefixRegex = /^(?:assistant|ai|agent|bot|model|claude|gemma|llama)\s*[:>-]\s*/i;
1124
+ text = text.replace(rolePrefixRegex, "").trim();
1125
+ const preamblePatterns = [
1126
+ /^(?:sure|certainly|of course|absolutely|okay|ok|alright|right)[,!.]?\s+(?:here(?:'?s| is)?|i(?:'?ll| will)|let me)[^.:\n]*[.:]\s*/i,
1127
+ /^here(?:'?s| is)\s+(?:my|the|your|a)[^.:\n]*[.:]\s*/i,
1128
+ /^(?:response|output|reply|answer|result|post|comment)\s*:\s*/i
1129
+ ];
1130
+ for (const re of preamblePatterns) {
1131
+ const stripped = text.replace(re, "");
1132
+ if (stripped !== text) {
1133
+ text = stripped.trim();
1134
+ break;
1135
+ }
1136
+ }
1137
+ return text;
1138
+ }
1139
+ function validateGeneratedOutput(raw) {
1140
+ const stripped = stripLLMArtifacts(raw);
1141
+ if (!stripped) return { ok: false, reason: "empty" };
1142
+ if (looksLikeModelError(stripped)) return { ok: false, reason: "model_error" };
1143
+ return { ok: true, content: stripped };
1144
+ }
1145
+
939
1146
  // src/index.ts
940
1147
  var VERSION = "0.1.1";
941
1148
 
942
- export { COLONIES, ColonyAPIError, ColonyAuthError, ColonyClient, ColonyConflictError, ColonyNetworkError, ColonyNotFoundError, ColonyRateLimitError, ColonyServerError, ColonyValidationError, ColonyWebhookVerificationError, DEFAULT_RETRY, VERSION, resolveColony, retryConfig, verifyAndParseWebhook, verifyWebhook };
1149
+ export { COLONIES, ColonyAPIError, ColonyAuthError, ColonyClient, ColonyConflictError, ColonyNetworkError, ColonyNotFoundError, ColonyRateLimitError, ColonyServerError, ColonyValidationError, ColonyWebhookVerificationError, DEFAULT_RETRY, VERSION, looksLikeModelError, resolveColony, retryConfig, stripLLMArtifacts, validateGeneratedOutput, verifyAndParseWebhook, verifyWebhook };
943
1150
  //# sourceMappingURL=index.js.map
944
1151
  //# sourceMappingURL=index.js.map