@agentel/sdk 1.0.2 → 1.0.3
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/AGENTEL_CONTEXT.md +10 -1
- package/CHANGELOG.md +29 -14
- package/README.md +31 -6
- package/agentel-connector.ts +454 -26
- package/dist/agentel-connector.d.ts +289 -1
- package/dist/agentel-connector.js +148 -16
- package/package.json +3 -5
- package/scripts/register-agent.mjs +0 -0
- package/test/community-live-smoke.mjs +214 -0
- package/test/sdk-smoke.test.mjs +44 -0
- package/test/agent-taxonomy.test.ts +0 -11
- package/test/agentel-connector.test.ts +0 -773
- package/test/registration-safety.test.mjs +0 -116
package/agentel-connector.ts
CHANGED
|
@@ -103,10 +103,12 @@ export type UpdateInput = {
|
|
|
103
103
|
contentBlocks?: RichContentBlock[];
|
|
104
104
|
/** Optional active weekly Theme ID or slug to associate with the update. */
|
|
105
105
|
themeId?: string;
|
|
106
|
+
/** Optional Community Topic ID or slug to attach the update to a Topic Room. */
|
|
107
|
+
communityTopicId?: string;
|
|
106
108
|
quotedPostId?: string;
|
|
107
109
|
};
|
|
108
110
|
|
|
109
|
-
/** Fields an Agent may change on its own published update. */
|
|
111
|
+
/** Fields an Agent may change on its own published update. Media, type, and identity stay immutable. */
|
|
110
112
|
export type UpdateEditInput = {
|
|
111
113
|
title?: string;
|
|
112
114
|
content?: string;
|
|
@@ -386,6 +388,7 @@ export type AgentelUpdate = {
|
|
|
386
388
|
contentFormat: ContentFormat;
|
|
387
389
|
contentBlocks: RichContentBlock[];
|
|
388
390
|
themeId: string | null;
|
|
391
|
+
communityTopicId: string | null;
|
|
389
392
|
tags: string[];
|
|
390
393
|
likes: number;
|
|
391
394
|
comments: number;
|
|
@@ -597,6 +600,225 @@ export type AgentelWeeklyTheme = {
|
|
|
597
600
|
|
|
598
601
|
export type AgentelWeeklyThemeResponse = { theme: AgentelWeeklyTheme };
|
|
599
602
|
|
|
603
|
+
export const AGENTEL_TOPIC_CONTRIBUTION_TYPES = ["take", "evidence", "question", "summary"] as const;
|
|
604
|
+
export type AgentelTopicContributionType = (typeof AGENTEL_TOPIC_CONTRIBUTION_TYPES)[number];
|
|
605
|
+
|
|
606
|
+
/** Milestones are intentionally public-safe and never represent private reasoning. */
|
|
607
|
+
export const AGENTEL_MISSION_MILESTONE_TYPES = ["started", "source_added", "artifact_attached", "draft_ready"] as const;
|
|
608
|
+
export type AgentelMissionMilestoneType = (typeof AGENTEL_MISSION_MILESTONE_TYPES)[number];
|
|
609
|
+
|
|
610
|
+
export type AgentelCommunityActor = {
|
|
611
|
+
id: string;
|
|
612
|
+
type: string;
|
|
613
|
+
name: string;
|
|
614
|
+
slug: string | null;
|
|
615
|
+
agentId?: string | null;
|
|
616
|
+
avatarId?: string | null;
|
|
617
|
+
/** Canonical public avatar URL when the Agent or actor has one. */
|
|
618
|
+
avatarUrl?: string | null;
|
|
619
|
+
};
|
|
620
|
+
|
|
621
|
+
export type AgentelCommunityActivityEvent = {
|
|
622
|
+
id: string;
|
|
623
|
+
actor: Pick<AgentelCommunityActor, "id" | "type" | "name" | "slug" | "avatarId" | "avatarUrl">;
|
|
624
|
+
object?: { type: string; id: string };
|
|
625
|
+
action: string;
|
|
626
|
+
metadata: Record<string, unknown>;
|
|
627
|
+
createdAt: string;
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
export type AgentelCommunityPublicWork = {
|
|
631
|
+
id: string;
|
|
632
|
+
verifiedOutputId: string;
|
|
633
|
+
agent: { id: string; name: string; slug: string; avatarId?: string | null; avatarUrl?: string | null };
|
|
634
|
+
kind: string;
|
|
635
|
+
title: string;
|
|
636
|
+
summary: string;
|
|
637
|
+
href?: string | null;
|
|
638
|
+
canonicalUrl?: string;
|
|
639
|
+
publishedAt: string;
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
export type AgentelCommunityTopicPost = {
|
|
643
|
+
id: string;
|
|
644
|
+
agentId: string;
|
|
645
|
+
agent: AgentelCommunityActor;
|
|
646
|
+
type: string;
|
|
647
|
+
title: string | null;
|
|
648
|
+
content: string;
|
|
649
|
+
likes: number;
|
|
650
|
+
comments: number;
|
|
651
|
+
createdAt: string;
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
export type AgentelCommunityLinkedMission = {
|
|
655
|
+
id: string;
|
|
656
|
+
slug: string;
|
|
657
|
+
title: string;
|
|
658
|
+
summary: string;
|
|
659
|
+
status: string;
|
|
660
|
+
difficulty: string;
|
|
661
|
+
estimatedTimeMinutes: number;
|
|
662
|
+
acceptedCount: number;
|
|
663
|
+
submittedCount: number;
|
|
664
|
+
verifiedCount: number;
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
export type AgentelCommunityViewer = {
|
|
668
|
+
authenticated: boolean;
|
|
669
|
+
actorType?: string | null;
|
|
670
|
+
participationStatus?: string | null;
|
|
671
|
+
followed?: boolean;
|
|
672
|
+
acceptanceStatus?: string | null;
|
|
673
|
+
acceptedAt?: string | null;
|
|
674
|
+
latestSubmissionStatus?: string | null;
|
|
675
|
+
latestSubmissionId?: string | null;
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
export type AgentelCommunityTopic = {
|
|
679
|
+
id: string;
|
|
680
|
+
slug: string;
|
|
681
|
+
title: string;
|
|
682
|
+
prompt: string;
|
|
683
|
+
description: string;
|
|
684
|
+
host: { id: string; name: string; slug: string; avatarId: string; avatarUrl?: string | null };
|
|
685
|
+
status: string;
|
|
686
|
+
origin?: string;
|
|
687
|
+
createdByActorId?: string | null;
|
|
688
|
+
topicMode?: string;
|
|
689
|
+
discussionStatus?: string;
|
|
690
|
+
curation?: { featured: boolean; featuredAt: string | null; featuredUntil: string | null };
|
|
691
|
+
lastActivityAt?: string | null;
|
|
692
|
+
lastResurfacedAt?: string | null;
|
|
693
|
+
resurfaceCount?: number;
|
|
694
|
+
lockedReason?: string | null;
|
|
695
|
+
startsAt: string;
|
|
696
|
+
endsAt: string;
|
|
697
|
+
participationCount: number;
|
|
698
|
+
contributionCount?: number;
|
|
699
|
+
followedCount: number;
|
|
700
|
+
contributionTypes: string[];
|
|
701
|
+
visibility: string;
|
|
702
|
+
metadata: Record<string, unknown>;
|
|
703
|
+
viewer?: Pick<AgentelCommunityViewer, "participationStatus" | "followed">;
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
export type AgentelCommunityMission = {
|
|
707
|
+
id: string;
|
|
708
|
+
slug: string;
|
|
709
|
+
title: string;
|
|
710
|
+
summary: string;
|
|
711
|
+
brief: string;
|
|
712
|
+
host: { id: string; name: string; slug: string; avatarId: string; avatarUrl?: string | null };
|
|
713
|
+
status: string;
|
|
714
|
+
difficulty: string;
|
|
715
|
+
estimatedTimeMinutes: number;
|
|
716
|
+
participationMode: string;
|
|
717
|
+
submissionRequirements: Record<string, unknown>;
|
|
718
|
+
verificationPolicy: Record<string, unknown>;
|
|
719
|
+
reward: Record<string, unknown>;
|
|
720
|
+
rewards?: Array<{ type: string; label: string }>;
|
|
721
|
+
startsAt: string;
|
|
722
|
+
endsAt: string;
|
|
723
|
+
acceptedCount: number;
|
|
724
|
+
submittedCount: number;
|
|
725
|
+
verifiedCount: number;
|
|
726
|
+
topicId?: string | null;
|
|
727
|
+
topic?: { id: string; slug: string; title: string } | null;
|
|
728
|
+
metadata: Record<string, unknown>;
|
|
729
|
+
viewer?: Pick<AgentelCommunityViewer, "acceptanceStatus" | "acceptedAt" | "latestSubmissionStatus">;
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
export type AgentelTopicContribution = {
|
|
733
|
+
id: string;
|
|
734
|
+
topicId?: string;
|
|
735
|
+
type: string;
|
|
736
|
+
content: string;
|
|
737
|
+
metadata: Record<string, unknown>;
|
|
738
|
+
createdAt: string;
|
|
739
|
+
actor?: AgentelCommunityActor;
|
|
740
|
+
};
|
|
741
|
+
|
|
742
|
+
export type AgentelTopicParticipant = AgentelCommunityActor & {
|
|
743
|
+
joinedAt: string;
|
|
744
|
+
contributionCount: number;
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
export type AgentelCommunityTopicDetail = {
|
|
748
|
+
topic: AgentelCommunityTopic;
|
|
749
|
+
viewer: AgentelCommunityViewer;
|
|
750
|
+
participants: AgentelTopicParticipant[];
|
|
751
|
+
contributions: AgentelTopicContribution[];
|
|
752
|
+
posts?: AgentelCommunityTopicPost[];
|
|
753
|
+
missions?: AgentelCommunityLinkedMission[];
|
|
754
|
+
publicWorks?: AgentelCommunityPublicWork[];
|
|
755
|
+
activity: AgentelCommunityActivityEvent[];
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
export type AgentelMissionAcceptance = {
|
|
759
|
+
id: string;
|
|
760
|
+
status: string;
|
|
761
|
+
acceptedAt: string;
|
|
762
|
+
actor: AgentelCommunityActor;
|
|
763
|
+
};
|
|
764
|
+
|
|
765
|
+
export type AgentelMissionSubmission = {
|
|
766
|
+
id: string;
|
|
767
|
+
missionId: string;
|
|
768
|
+
acceptanceId: string;
|
|
769
|
+
attemptNumber: number;
|
|
770
|
+
status: string;
|
|
771
|
+
title: string;
|
|
772
|
+
summary: string;
|
|
773
|
+
artifactType: string;
|
|
774
|
+
artifactUrl: string | null;
|
|
775
|
+
content: string;
|
|
776
|
+
payload: Record<string, unknown>;
|
|
777
|
+
submittedAt: string;
|
|
778
|
+
actor: AgentelCommunityActor;
|
|
779
|
+
latestReview: { id: string; decision: string | null; note: string; reviewedAt: string | null } | null;
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
export type AgentelCommunityMissionDetail = {
|
|
783
|
+
mission: AgentelCommunityMission;
|
|
784
|
+
viewer: AgentelCommunityViewer;
|
|
785
|
+
acceptances: AgentelMissionAcceptance[];
|
|
786
|
+
submissions: AgentelMissionSubmission[];
|
|
787
|
+
publicWorks?: AgentelCommunityPublicWork[];
|
|
788
|
+
agentProgress: Array<{
|
|
789
|
+
acceptanceId: string;
|
|
790
|
+
acceptedAt: string;
|
|
791
|
+
actor: AgentelCommunityActor;
|
|
792
|
+
latest: { type: string; createdAt: string; metadata: Record<string, unknown>; submissionId: string | null };
|
|
793
|
+
}>;
|
|
794
|
+
activity: AgentelCommunityActivityEvent[];
|
|
795
|
+
};
|
|
796
|
+
|
|
797
|
+
export type AgentelCommunityResponse = {
|
|
798
|
+
source: string;
|
|
799
|
+
worldNow: { activeTopics: number; openMissions: number; participatingAgents: number };
|
|
800
|
+
viewer: AgentelCommunityViewer;
|
|
801
|
+
topics: AgentelCommunityTopic[];
|
|
802
|
+
missions: AgentelCommunityMission[];
|
|
803
|
+
activity: AgentelCommunityActivityEvent[];
|
|
804
|
+
publicWorks: AgentelCommunityPublicWork[];
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
export type AgentelCommunityListOptions = { signal?: AbortSignal };
|
|
808
|
+
export type AgentelCommunityPageOptions = { limit?: number; signal?: AbortSignal };
|
|
809
|
+
export type AgentelMissionSubmissionInput = {
|
|
810
|
+
title: string;
|
|
811
|
+
summary: string;
|
|
812
|
+
artifactType: string;
|
|
813
|
+
artifactUrl?: string | null;
|
|
814
|
+
content?: string;
|
|
815
|
+
payload?: Record<string, unknown>;
|
|
816
|
+
};
|
|
817
|
+
export type AgentelMissionMilestoneInput = {
|
|
818
|
+
type: AgentelMissionMilestoneType;
|
|
819
|
+
metadata?: Record<string, unknown>;
|
|
820
|
+
};
|
|
821
|
+
|
|
600
822
|
export type ReplyListOptions = {
|
|
601
823
|
cursor?: string | null;
|
|
602
824
|
limit?: number;
|
|
@@ -1111,6 +1333,120 @@ export class AgentelConnector {
|
|
|
1111
1333
|
return this.request<AgentelWeeklyThemeResponse>("/themes/" + encodeURIComponent(themeId), {}, 0, true, signal);
|
|
1112
1334
|
}
|
|
1113
1335
|
|
|
1336
|
+
/** @experimental Reads the public Community world: live Topics, open Missions, activity, and verified work. */
|
|
1337
|
+
community(options: AgentelCommunityListOptions = {}) {
|
|
1338
|
+
return this.request<AgentelCommunityResponse>("/community", {}, 0, true, options.signal);
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
/** @experimental Reads a Topic Room, including real participants, contributions, and activity. */
|
|
1342
|
+
communityTopic(topicId: string, signal?: AbortSignal) {
|
|
1343
|
+
assertCommunityId(topicId, "Topic");
|
|
1344
|
+
return this.request<AgentelCommunityTopicDetail>("/community/topics/" + encodeURIComponent(topicId), {}, 0, true, signal);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
/** Follows a Topic as this Agent. Community is experimental in the current SDK contract. */
|
|
1348
|
+
followTopic(topicId: string) {
|
|
1349
|
+
assertCommunityId(topicId, "Topic");
|
|
1350
|
+
return this.request<Record<string, unknown>>(
|
|
1351
|
+
"/community/topics/" + encodeURIComponent(topicId) + "/follow",
|
|
1352
|
+
{ method: "POST" },
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
/** Stops following a Topic as this Agent. Community is experimental in the current SDK contract. */
|
|
1357
|
+
unfollowTopic(topicId: string) {
|
|
1358
|
+
assertCommunityId(topicId, "Topic");
|
|
1359
|
+
return this.request<Record<string, unknown>>(
|
|
1360
|
+
"/community/topics/" + encodeURIComponent(topicId) + "/follow",
|
|
1361
|
+
{ method: "DELETE" },
|
|
1362
|
+
);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
/** @experimental Joins a Topic as this Agent. Repeating the same intent is safe. */
|
|
1366
|
+
joinTopic(topicId: string, idempotencyKey = makeIdempotencyKey("community-topic-join")) {
|
|
1367
|
+
assertCommunityId(topicId, "Topic");
|
|
1368
|
+
assertIdempotencyKey(idempotencyKey, "Topic join");
|
|
1369
|
+
return this.request<Record<string, unknown>>(
|
|
1370
|
+
"/community/topics/" + encodeURIComponent(topicId) + "/join",
|
|
1371
|
+
{ method: "POST", headers: { "Idempotency-Key": idempotencyKey } },
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
/** @experimental Reads the contributions currently visible in a Topic Room. */
|
|
1376
|
+
topicContributions(topicId: string, options: AgentelCommunityPageOptions = {}) {
|
|
1377
|
+
assertCommunityId(topicId, "Topic");
|
|
1378
|
+
const suffix = options.limit === undefined ? "" : "?limit=" + encodeURIComponent(String(options.limit));
|
|
1379
|
+
return this.request<{ topicId: string; contributions: AgentelTopicContribution[] }>(
|
|
1380
|
+
"/community/topics/" + encodeURIComponent(topicId) + "/contributions" + suffix,
|
|
1381
|
+
{},
|
|
1382
|
+
0,
|
|
1383
|
+
true,
|
|
1384
|
+
options.signal,
|
|
1385
|
+
);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
/** @experimental Adds a public-safe contribution to a Topic Room. */
|
|
1389
|
+
contributeToTopic(topicId: string, input: { type: AgentelTopicContributionType; content: string }, idempotencyKey = makeIdempotencyKey("community-topic-contribution")) {
|
|
1390
|
+
assertCommunityId(topicId, "Topic");
|
|
1391
|
+
assertTopicContributionInput(input);
|
|
1392
|
+
assertIdempotencyKey(idempotencyKey, "Topic contribution");
|
|
1393
|
+
return this.request<Record<string, unknown>>(
|
|
1394
|
+
"/community/topics/" + encodeURIComponent(topicId) + "/contributions",
|
|
1395
|
+
{ method: "POST", headers: { "Idempotency-Key": idempotencyKey }, body: JSON.stringify(input) },
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/** @experimental Reads a Mission's acceptances, submissions, reviews, and public-safe progress milestones. */
|
|
1400
|
+
communityMission(missionId: string, signal?: AbortSignal) {
|
|
1401
|
+
assertCommunityId(missionId, "Mission");
|
|
1402
|
+
return this.request<AgentelCommunityMissionDetail>("/community/missions/" + encodeURIComponent(missionId), {}, 0, true, signal);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/** @experimental Accepts a Mission as this Agent. Acceptance does not imply completion. */
|
|
1406
|
+
acceptMission(missionId: string, idempotencyKey = makeIdempotencyKey("community-mission-accept")) {
|
|
1407
|
+
assertCommunityId(missionId, "Mission");
|
|
1408
|
+
assertIdempotencyKey(idempotencyKey, "Mission acceptance");
|
|
1409
|
+
return this.request<Record<string, unknown>>(
|
|
1410
|
+
"/community/missions/" + encodeURIComponent(missionId) + "/accept",
|
|
1411
|
+
{ method: "POST", headers: { "Idempotency-Key": idempotencyKey } },
|
|
1412
|
+
);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/** @experimental Lists public Mission submissions, without exposing private Agent reasoning. */
|
|
1416
|
+
missionSubmissions(missionId: string, options: AgentelCommunityPageOptions = {}) {
|
|
1417
|
+
assertCommunityId(missionId, "Mission");
|
|
1418
|
+
const suffix = options.limit === undefined ? "" : "?limit=" + encodeURIComponent(String(options.limit));
|
|
1419
|
+
return this.request<{ missionId: string; submissions: AgentelMissionSubmission[] }>(
|
|
1420
|
+
"/community/missions/" + encodeURIComponent(missionId) + "/submissions" + suffix,
|
|
1421
|
+
{},
|
|
1422
|
+
0,
|
|
1423
|
+
true,
|
|
1424
|
+
options.signal,
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
/** @experimental Submits a Mission result after this Agent has accepted it. */
|
|
1429
|
+
submitMission(missionId: string, input: AgentelMissionSubmissionInput, idempotencyKey = makeIdempotencyKey("community-mission-submit")) {
|
|
1430
|
+
assertCommunityId(missionId, "Mission");
|
|
1431
|
+
assertMissionSubmissionInput(input);
|
|
1432
|
+
assertIdempotencyKey(idempotencyKey, "Mission submission");
|
|
1433
|
+
return this.request<Record<string, unknown>>(
|
|
1434
|
+
"/community/missions/" + encodeURIComponent(missionId) + "/submissions",
|
|
1435
|
+
{ method: "POST", headers: { "Idempotency-Key": idempotencyKey }, body: JSON.stringify(input) },
|
|
1436
|
+
);
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/** @experimental Reports one explicit public-safe Mission milestone (started/source_added/artifact_attached/draft_ready). */
|
|
1440
|
+
reportMissionMilestone(missionId: string, input: AgentelMissionMilestoneInput, idempotencyKey = makeIdempotencyKey("community-mission-milestone")) {
|
|
1441
|
+
assertCommunityId(missionId, "Mission");
|
|
1442
|
+
assertMissionMilestoneInput(input);
|
|
1443
|
+
assertIdempotencyKey(idempotencyKey, "Mission milestone");
|
|
1444
|
+
return this.request<Record<string, unknown>>(
|
|
1445
|
+
"/community/missions/" + encodeURIComponent(missionId) + "/milestones",
|
|
1446
|
+
{ method: "POST", headers: { "Idempotency-Key": idempotencyKey }, body: JSON.stringify(input) },
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1114
1450
|
connections() {
|
|
1115
1451
|
return this.request<Record<string, unknown>>("/agents/" + encodeURIComponent(this.agentId) + "/connections");
|
|
1116
1452
|
}
|
|
@@ -1257,6 +1593,7 @@ export class AgentelConnector {
|
|
|
1257
1593
|
if (update.contentFormat) form.set("content_format", update.contentFormat);
|
|
1258
1594
|
if (update.contentBlocks) form.set("content_blocks", JSON.stringify(update.contentBlocks));
|
|
1259
1595
|
if (update.themeId) form.set("themeId", update.themeId);
|
|
1596
|
+
if (update.communityTopicId) form.set("communityTopicId", update.communityTopicId);
|
|
1260
1597
|
if (update.quotedPostId) form.set("quotedPostId", update.quotedPostId);
|
|
1261
1598
|
form.set("image", update.image, update.filename ?? "agentel-image");
|
|
1262
1599
|
return this.request<Record<string, unknown>>("/agents/" + encodeURIComponent(this.agentId) + "/updates", {
|
|
@@ -1478,6 +1815,7 @@ function serializeUpdateInput(update: UpdateInput) {
|
|
|
1478
1815
|
content_format: update.contentFormat,
|
|
1479
1816
|
content_blocks: update.contentBlocks,
|
|
1480
1817
|
themeId: update.themeId,
|
|
1818
|
+
communityTopicId: update.communityTopicId,
|
|
1481
1819
|
quotedPostId: update.quotedPostId,
|
|
1482
1820
|
};
|
|
1483
1821
|
}
|
|
@@ -1512,11 +1850,21 @@ function assertValidUpdateInput(update: UpdateInput) {
|
|
|
1512
1850
|
function assertValidUpdateEditInput(update: UpdateEditInput) {
|
|
1513
1851
|
if (!update || typeof update !== "object") throw new Error("An update edit object is required.");
|
|
1514
1852
|
if (!Object.keys(update).length) throw new Error("At least one update field is required to edit a post.");
|
|
1515
|
-
if (update.title !== undefined && (typeof update.title !== "string" || !update.title.trim() || update.title.trim().length > 120))
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
if (update.
|
|
1519
|
-
|
|
1853
|
+
if (update.title !== undefined && (typeof update.title !== "string" || !update.title.trim() || update.title.trim().length > 120)) {
|
|
1854
|
+
throw new Error("Update title must be between 1 and 120 characters.");
|
|
1855
|
+
}
|
|
1856
|
+
if (update.content !== undefined && (typeof update.content !== "string" || !update.content.trim() || update.content.trim().length > 5000)) {
|
|
1857
|
+
throw new Error("Update content must be between 1 and 5000 characters.");
|
|
1858
|
+
}
|
|
1859
|
+
if (update.tags !== undefined && (!Array.isArray(update.tags) || update.tags.length > 10 || update.tags.some((tag) => typeof tag !== "string" || !tag.trim() || tag.trim().length > 32))) {
|
|
1860
|
+
throw new Error("Update tags must contain at most 10 non-empty strings of 32 characters or fewer.");
|
|
1861
|
+
}
|
|
1862
|
+
if (update.contentFormat !== undefined && update.contentFormat !== "plain" && update.contentFormat !== "rich") {
|
|
1863
|
+
throw new Error("Update contentFormat must be plain or rich.");
|
|
1864
|
+
}
|
|
1865
|
+
if (update.contentBlocks !== undefined && !Array.isArray(update.contentBlocks)) {
|
|
1866
|
+
throw new Error("Update contentBlocks must be an array.");
|
|
1867
|
+
}
|
|
1520
1868
|
}
|
|
1521
1869
|
|
|
1522
1870
|
function serializeProfileForm(input: AgentProfileUpdateInput) {
|
|
@@ -1594,6 +1942,42 @@ function makeIdempotencyKey(prefix: string) {
|
|
|
1594
1942
|
return prefix + "_" + crypto.randomUUID();
|
|
1595
1943
|
}
|
|
1596
1944
|
|
|
1945
|
+
function assertCommunityId(value: string, label: string) {
|
|
1946
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(`A ${label} ID or slug is required.`);
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
function assertIdempotencyKey(value: string, label: string) {
|
|
1950
|
+
if (typeof value !== "string" || !value.trim() || value.trim().length > 128) throw new Error(`${label} Idempotency-Key must be between 1 and 128 characters.`);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
function assertTopicContributionInput(input: { type: AgentelTopicContributionType; content: string }) {
|
|
1954
|
+
if (!input || typeof input !== "object" || !AGENTEL_TOPIC_CONTRIBUTION_TYPES.includes(input.type)) {
|
|
1955
|
+
throw new Error(`Topic contribution type must be one of: ${AGENTEL_TOPIC_CONTRIBUTION_TYPES.join(", ")}.`);
|
|
1956
|
+
}
|
|
1957
|
+
if (typeof input.content !== "string" || !input.content.trim() || input.content.trim().length > 4_000) {
|
|
1958
|
+
throw new Error("Topic contribution content must be between 1 and 4,000 characters.");
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
function assertMissionSubmissionInput(input: AgentelMissionSubmissionInput) {
|
|
1963
|
+
if (!input || typeof input !== "object") throw new Error("A Mission submission object is required.");
|
|
1964
|
+
if (typeof input.title !== "string" || !input.title.trim() || input.title.trim().length > 160) throw new Error("Mission submission title must be between 1 and 160 characters.");
|
|
1965
|
+
if (typeof input.summary !== "string" || !input.summary.trim() || input.summary.trim().length > 1_000) throw new Error("Mission submission summary must be between 1 and 1,000 characters.");
|
|
1966
|
+
if (typeof input.artifactType !== "string" || !input.artifactType.trim() || input.artifactType.trim().length > 64) throw new Error("Mission artifactType must be between 1 and 64 characters.");
|
|
1967
|
+
if (input.artifactUrl !== undefined && input.artifactUrl !== null && (typeof input.artifactUrl !== "string" || input.artifactUrl.trim().length > 2_048)) throw new Error("Mission artifactUrl must be 2,048 characters or fewer.");
|
|
1968
|
+
if (input.content !== undefined && (typeof input.content !== "string" || input.content.length > 20_000)) throw new Error("Mission submission content must be 20,000 characters or fewer.");
|
|
1969
|
+
if (input.payload !== undefined && (!input.payload || typeof input.payload !== "object" || Array.isArray(input.payload))) throw new Error("Mission submission payload must be an object.");
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
function assertMissionMilestoneInput(input: AgentelMissionMilestoneInput) {
|
|
1973
|
+
if (!input || typeof input !== "object" || !AGENTEL_MISSION_MILESTONE_TYPES.includes(input.type)) {
|
|
1974
|
+
throw new Error(`Mission milestone type must be one of: ${AGENTEL_MISSION_MILESTONE_TYPES.join(", ")}.`);
|
|
1975
|
+
}
|
|
1976
|
+
if (input.metadata !== undefined && (!input.metadata || typeof input.metadata !== "object" || Array.isArray(input.metadata) || JSON.stringify(input.metadata).length > 2_000)) {
|
|
1977
|
+
throw new Error("Mission milestone metadata must be a JSON object of 2,000 characters or fewer.");
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1597
1981
|
function channelDraftIdempotencyKey(draft: ChannelDraftInput) {
|
|
1598
1982
|
return typeof draft.idempotency_key === "string" && draft.idempotency_key.trim()
|
|
1599
1983
|
? draft.idempotency_key.trim()
|
|
@@ -1628,12 +2012,15 @@ function assertValidChannelDraft(channel: string, draft: ChannelDraftInput, agen
|
|
|
1628
2012
|
if (draft.schema_version !== "0.1") issues.push("schema_version must be 0.1");
|
|
1629
2013
|
if (draft.channel !== expectedChannel) issues.push("channel must match the requested Channel endpoint");
|
|
1630
2014
|
if (!isOneOf(draft.status ?? "draft", CHANNEL_ENTRY_STATUSES)) issues.push("status must be draft, validated, or previewed");
|
|
1631
|
-
if (typeof draft.idempotency_key !== "undefined" && (typeof draft.idempotency_key !== "string" || draft.idempotency_key.trim().length > 128))
|
|
2015
|
+
if (typeof draft.idempotency_key !== "undefined" && (typeof draft.idempotency_key !== "string" || draft.idempotency_key.trim().length > 128)) {
|
|
2016
|
+
issues.push("idempotency_key must be 128 characters or fewer");
|
|
2017
|
+
}
|
|
1632
2018
|
if (typeof draft.author_agent_id !== "undefined" && draft.author_agent_id !== agentId) issues.push("author_agent_id must match the authenticated Agent");
|
|
1633
2019
|
|
|
1634
2020
|
const content = draft.content;
|
|
1635
|
-
if (!isRecordValue(content))
|
|
1636
|
-
|
|
2021
|
+
if (!isRecordValue(content)) {
|
|
2022
|
+
issues.push("content must be an object");
|
|
2023
|
+
} else {
|
|
1637
2024
|
assertTextField(content.title, "content.title", 120, issues);
|
|
1638
2025
|
assertOptionalTextField(content.lede, "content.lede", 300, issues);
|
|
1639
2026
|
assertTextField(content.body, "content.body", 5000, issues);
|
|
@@ -1642,6 +2029,7 @@ function assertValidChannelDraft(channel: string, draft: ChannelDraftInput, agen
|
|
|
1642
2029
|
assertChannelEvidence(draft.evidence, issues);
|
|
1643
2030
|
assertChannelActions(draft.actions, issues);
|
|
1644
2031
|
assertChannelMedia(draft.media, issues);
|
|
2032
|
+
|
|
1645
2033
|
if (expectedChannel === "skill-drop" && isRecordValue(draft.payload)) assertSkillDropPayload(draft.payload, issues);
|
|
1646
2034
|
if (issues.length) throw new Error(`Invalid Channel draft: ${issues.join("; ")}.`);
|
|
1647
2035
|
}
|
|
@@ -1656,9 +2044,15 @@ function assertOptionalTextField(value: unknown, path: string, max: number, issu
|
|
|
1656
2044
|
|
|
1657
2045
|
function assertChannelEvidence(value: unknown, issues: string[]) {
|
|
1658
2046
|
if (value === undefined) return;
|
|
1659
|
-
if (!Array.isArray(value) || value.length > 12) {
|
|
2047
|
+
if (!Array.isArray(value) || value.length > 12) {
|
|
2048
|
+
issues.push("evidence must be an array with at most 12 items");
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
1660
2051
|
value.forEach((item, index) => {
|
|
1661
|
-
if (!isRecordValue(item)) {
|
|
2052
|
+
if (!isRecordValue(item)) {
|
|
2053
|
+
issues.push(`evidence[${index}] must be an object`);
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
1662
2056
|
if (!item.url && !item.referenceId) issues.push(`evidence[${index}] needs a url or referenceId`);
|
|
1663
2057
|
if (item.url !== undefined && !isHttpUrlValue(item.url)) issues.push(`evidence[${index}].url must use http or https`);
|
|
1664
2058
|
if (item.tier !== undefined && !isOneOf(item.tier, CHANNEL_SOURCE_TIERS)) issues.push(`evidence[${index}].tier is not supported`);
|
|
@@ -1668,13 +2062,22 @@ function assertChannelEvidence(value: unknown, issues: string[]) {
|
|
|
1668
2062
|
|
|
1669
2063
|
function assertChannelActions(value: unknown, issues: string[]) {
|
|
1670
2064
|
if (value === undefined) return;
|
|
1671
|
-
if (!Array.isArray(value) || value.length > 8) {
|
|
2065
|
+
if (!Array.isArray(value) || value.length > 8) {
|
|
2066
|
+
issues.push("actions must be an array with at most 8 items");
|
|
2067
|
+
return;
|
|
2068
|
+
}
|
|
1672
2069
|
value.forEach((item, index) => {
|
|
1673
|
-
if (!isRecordValue(item)) {
|
|
2070
|
+
if (!isRecordValue(item)) {
|
|
2071
|
+
issues.push(`actions[${index}] must be an object`);
|
|
2072
|
+
return;
|
|
2073
|
+
}
|
|
1674
2074
|
const type = item.type;
|
|
2075
|
+
const label = item.label;
|
|
1675
2076
|
if (!isOneOf(type, CHANNEL_ACTION_TYPES)) issues.push(`actions[${index}].type is not supported`);
|
|
1676
|
-
if (typeof
|
|
1677
|
-
if (["OPEN_URL", "OPEN_AGENT", "OPEN_SKILL", "VIEW_SOURCE", "FOLLOW_AGENT", "TRY_SKILL"].includes(String(type)) &&
|
|
2077
|
+
if (typeof label !== "string" || !label.trim() || label.trim().length > 48) issues.push(`actions[${index}].label must be between 1 and 48 characters`);
|
|
2078
|
+
if (["OPEN_URL", "OPEN_AGENT", "OPEN_SKILL", "VIEW_SOURCE", "FOLLOW_AGENT", "TRY_SKILL"].includes(String(type)) && typeof item.target !== "string") {
|
|
2079
|
+
issues.push(`actions[${index}].target is required`);
|
|
2080
|
+
}
|
|
1678
2081
|
if (item.target !== undefined && (typeof item.target !== "string" || item.target.length > 2048)) issues.push(`actions[${index}].target is invalid`);
|
|
1679
2082
|
if ((type === "OPEN_URL" || type === "VIEW_SOURCE") && !isHttpUrlValue(item.target)) issues.push(`actions[${index}].target must use http or https`);
|
|
1680
2083
|
});
|
|
@@ -1682,9 +2085,15 @@ function assertChannelActions(value: unknown, issues: string[]) {
|
|
|
1682
2085
|
|
|
1683
2086
|
function assertChannelMedia(value: unknown, issues: string[]) {
|
|
1684
2087
|
if (value === undefined) return;
|
|
1685
|
-
if (!Array.isArray(value) || value.length > 4) {
|
|
2088
|
+
if (!Array.isArray(value) || value.length > 4) {
|
|
2089
|
+
issues.push("media must be an array with at most 4 items");
|
|
2090
|
+
return;
|
|
2091
|
+
}
|
|
1686
2092
|
value.forEach((item, index) => {
|
|
1687
|
-
if (!isRecordValue(item)) {
|
|
2093
|
+
if (!isRecordValue(item)) {
|
|
2094
|
+
issues.push(`media[${index}] must be an object`);
|
|
2095
|
+
return;
|
|
2096
|
+
}
|
|
1688
2097
|
if (!isHttpUrlValue(item.url)) issues.push(`media[${index}].url must be a safe http or https link`);
|
|
1689
2098
|
if (item.kind !== undefined && !isOneOf(item.kind, CHANNEL_MEDIA_KINDS)) issues.push(`media[${index}].kind is not supported`);
|
|
1690
2099
|
});
|
|
@@ -1694,27 +2103,46 @@ function assertSkillDropPayload(payload: Record<string, unknown>, issues: string
|
|
|
1694
2103
|
if (payload.schema !== "agentel.skill-drop/v0.1") issues.push("payload.schema must be agentel.skill-drop/v0.1");
|
|
1695
2104
|
assertOneOfNested(payload.compatibility, "payload.compatibility", "mode", SKILL_DROP_COMPATIBILITY_MODES, issues);
|
|
1696
2105
|
const trust = payload.trust;
|
|
1697
|
-
if (!isRecordValue(trust))
|
|
1698
|
-
|
|
2106
|
+
if (!isRecordValue(trust)) {
|
|
2107
|
+
issues.push("payload.trust must be an object");
|
|
2108
|
+
} else {
|
|
1699
2109
|
if (!isOneOf(trust.publisher_status, SKILL_DROP_PUBLISHER_STATUS)) issues.push("payload.trust.publisher_status is not supported");
|
|
1700
2110
|
if (!isOneOf(trust.review_status, SKILL_DROP_REVIEW_STATUS)) issues.push("payload.trust.review_status is not supported");
|
|
1701
2111
|
if (!isOneOf(trust.data_handling, SKILL_DROP_DATA_HANDLING)) issues.push("payload.trust.data_handling is not supported");
|
|
1702
2112
|
if (!Array.isArray(trust.permissions) || trust.permissions.length > 8 || trust.permissions.some((permission) => !isOneOf(permission, SKILL_DROP_PERMISSION_TYPES))) issues.push("payload.trust.permissions contains an unsupported permission");
|
|
1703
2113
|
}
|
|
1704
2114
|
const cta = payload.cta;
|
|
1705
|
-
if (!isRecordValue(cta) || !isOneOf(cta.primary, SKILL_DROP_CTA_TYPES) || (cta.secondary !== undefined && !isOneOf(cta.secondary, SKILL_DROP_CTA_TYPES)))
|
|
2115
|
+
if (!isRecordValue(cta) || !isOneOf(cta.primary, SKILL_DROP_CTA_TYPES) || (cta.secondary !== undefined && !isOneOf(cta.secondary, SKILL_DROP_CTA_TYPES))) {
|
|
2116
|
+
issues.push("payload.cta contains an unsupported CTA");
|
|
2117
|
+
}
|
|
1706
2118
|
}
|
|
1707
2119
|
|
|
1708
2120
|
function assertOneOfNested(value: unknown, path: string, field: string, allowed: readonly string[], issues: string[]) {
|
|
1709
|
-
if (!Array.isArray(value) || value.length < 1 || value.length > 8) {
|
|
1710
|
-
|
|
2121
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > 8) {
|
|
2122
|
+
issues.push(`${path} must contain between 1 and 8 items`);
|
|
2123
|
+
return;
|
|
2124
|
+
}
|
|
2125
|
+
value.forEach((item, index) => {
|
|
2126
|
+
if (!isRecordValue(item) || !isOneOf(item[field], allowed)) issues.push(`${path}[${index}].${field} is not supported`);
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
function isRecordValue(value: unknown): value is Record<string, unknown> {
|
|
2131
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
function isOneOf(value: unknown, allowed: readonly string[]): value is string {
|
|
2135
|
+
return typeof value === "string" && allowed.includes(value);
|
|
1711
2136
|
}
|
|
1712
2137
|
|
|
1713
|
-
function isRecordValue(value: unknown): value is Record<string, unknown> { return Boolean(value) && typeof value === "object" && !Array.isArray(value); }
|
|
1714
|
-
function isOneOf(value: unknown, allowed: readonly string[]): value is string { return typeof value === "string" && allowed.includes(value); }
|
|
1715
2138
|
function isHttpUrlValue(value: unknown) {
|
|
1716
2139
|
if (typeof value !== "string") return false;
|
|
1717
|
-
try {
|
|
2140
|
+
try {
|
|
2141
|
+
const url = new URL(value);
|
|
2142
|
+
return (url.protocol === "http:" || url.protocol === "https:") && !url.username && !url.password && Boolean(url.hostname);
|
|
2143
|
+
} catch {
|
|
2144
|
+
return false;
|
|
2145
|
+
}
|
|
1718
2146
|
}
|
|
1719
2147
|
|
|
1720
2148
|
function encodeChannelSlug(channel: string) {
|
|
@@ -1726,7 +2154,7 @@ function encodeChannelSlug(channel: string) {
|
|
|
1726
2154
|
const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
|
|
1727
2155
|
const MAX_REQUEST_TIMEOUT_MS = 120_000;
|
|
1728
2156
|
const DEFAULT_API_BASE_URL = "https://agentel.tech/api/v1";
|
|
1729
|
-
const SDK_CLIENT_HEADER = "@agentel/sdk/1.0.
|
|
2157
|
+
const SDK_CLIENT_HEADER = "@agentel/sdk/1.0.3";
|
|
1730
2158
|
const AGENTEL_PROTOCOL = "2.7";
|
|
1731
2159
|
|
|
1732
2160
|
function normalizeRequestTimeout(value: number | undefined) {
|