@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.
@@ -0,0 +1,214 @@
1
+ import assert from "node:assert/strict";
2
+ import { AgentelConnector } from "../dist/agentel-connector.js";
3
+
4
+ /**
5
+ * Controlled Community compatibility test. It never creates data unless the
6
+ * caller opts into the write phase with the explicit confirmation below.
7
+ * Keep credentials in the shell or a private --env-file; this script never
8
+ * prints them.
9
+ */
10
+
11
+ const env = process.env;
12
+ const baseUrl = env.AGENTEL_API_BASE_URL?.trim();
13
+ const missing = [
14
+ "AGENTEL_API_BASE_URL",
15
+ "AGENTEL_E2E_OFFICIAL_AGENT_ID",
16
+ "AGENTEL_E2E_OFFICIAL_API_KEY",
17
+ "AGENTEL_E2E_PARTICIPANT_AGENT_ID",
18
+ "AGENTEL_E2E_PARTICIPANT_API_KEY",
19
+ "AGENTEL_E2E_TOPIC_ID",
20
+ "AGENTEL_E2E_MISSION_ID",
21
+ ].filter((name) => !env[name]?.trim());
22
+
23
+ if (missing.length) {
24
+ console.error(JSON.stringify({
25
+ status: "not_configured",
26
+ missing,
27
+ hint: "Provide two isolated Agent credentials and a dedicated public test Topic/Mission before running the live Community smoke test.",
28
+ }));
29
+ process.exit(2);
30
+ }
31
+
32
+ const official = new AgentelConnector({
33
+ baseUrl,
34
+ agentId: env.AGENTEL_E2E_OFFICIAL_AGENT_ID,
35
+ apiKey: env.AGENTEL_E2E_OFFICIAL_API_KEY,
36
+ });
37
+ const participant = new AgentelConnector({
38
+ baseUrl,
39
+ agentId: env.AGENTEL_E2E_PARTICIPANT_AGENT_ID,
40
+ apiKey: env.AGENTEL_E2E_PARTICIPANT_API_KEY,
41
+ });
42
+
43
+ const topicId = env.AGENTEL_E2E_TOPIC_ID;
44
+ const missionId = env.AGENTEL_E2E_MISSION_ID;
45
+ const runId = env.AGENTEL_E2E_RUN_ID?.trim() || `sdk-${Date.now().toString(36)}`;
46
+
47
+ try {
48
+ const identities = await Promise.all([
49
+ readIdentity("official", official),
50
+ readIdentity("participant", participant),
51
+ ]);
52
+
53
+ const readSnapshots = await Promise.all([
54
+ readCommunity("official", official, topicId, missionId),
55
+ readCommunity("participant", participant, topicId, missionId),
56
+ ]);
57
+
58
+ const result = {
59
+ status: "read_pass",
60
+ identities,
61
+ readSnapshots,
62
+ writes: "not_run",
63
+ };
64
+
65
+ if (env.AGENTEL_E2E_WRITE_CONFIRM === "I_UNDERSTAND_DEDICATED_TEST_DATA") {
66
+ result.writes = await runWriteFlow({ officialApiKey: env.AGENTEL_E2E_OFFICIAL_API_KEY, participant, topicId, missionId, runId, baseUrl });
67
+ }
68
+
69
+ console.log(JSON.stringify(result));
70
+ } catch (error) {
71
+ console.error(JSON.stringify({ status: "failed", error: safeError(error) }));
72
+ process.exitCode = 1;
73
+ }
74
+
75
+ async function readIdentity(label, connector) {
76
+ const response = await connector.me();
77
+ assert.equal(response.agent.id, connector.currentAgentId, `${label} credential must resolve to its configured Agent`);
78
+ return { label, id: response.agent.id, slug: response.agent.slug, name: response.agent.name };
79
+ }
80
+
81
+ async function readCommunity(label, connector, topicId, missionId) {
82
+ const [world, topic, mission, profile] = await Promise.all([
83
+ connector.community(),
84
+ connector.communityTopic(topicId),
85
+ connector.communityMission(missionId),
86
+ connector.profile(),
87
+ ]);
88
+ for (const item of [world.topics?.[0]?.host?.avatarUrl, world.missions?.[0]?.host?.avatarUrl, world.publicWorks?.[0]?.agent?.avatarUrl]) {
89
+ assert.ok(item === undefined || item === null || typeof item === "string", `${label} received an invalid avatarUrl type`);
90
+ }
91
+ const officialTopicHost = world.topics?.find((item) => item.host?.id === "agentel-official")?.host;
92
+ if (officialTopicHost) assert.ok(typeof officialTopicHost.avatarUrl === "string" && officialTopicHost.avatarUrl.length > 0, `${label} did not receive the canonical official host avatar URL`);
93
+ const visibleWork = world.publicWorks?.find((work) => work.agent?.avatarUrl);
94
+ if (visibleWork) assert.ok(typeof visibleWork.agent.avatarUrl === "string" && visibleWork.agent.avatarUrl.length > 0, `${label} did not receive the canonical public-work avatar URL`);
95
+ return {
96
+ label,
97
+ topics: world.topics?.length ?? 0,
98
+ missions: world.missions?.length ?? 0,
99
+ activity: world.activity?.length ?? 0,
100
+ topic: {
101
+ slug: topic.topic?.slug ?? null,
102
+ participants: topic.participants?.length ?? 0,
103
+ contributions: topic.contributions?.length ?? 0,
104
+ linkedMissions: topic.missions?.length ?? 0,
105
+ },
106
+ mission: {
107
+ slug: mission.mission?.slug ?? null,
108
+ acceptances: mission.acceptances?.length ?? 0,
109
+ submissions: mission.submissions?.length ?? 0,
110
+ publicWorks: mission.publicWorks?.length ?? 0,
111
+ },
112
+ profile: { id: profile.agent?.id ?? null, avatarUrl: Boolean(profile.agent?.avatarUrl) },
113
+ };
114
+ }
115
+
116
+ async function runWriteFlow({ officialApiKey, participant, topicId, missionId, runId, baseUrl }) {
117
+ const keys = {
118
+ follow: `${runId}:follow`,
119
+ join: `${runId}:join`,
120
+ contribution: `${runId}:contribution`,
121
+ accept: `${runId}:accept`,
122
+ milestone: `${runId}:milestone`,
123
+ submit: `${runId}:submit`,
124
+ review: `${runId}:review`,
125
+ };
126
+
127
+ await participant.followTopic(topicId);
128
+ await participant.unfollowTopic(topicId);
129
+ const joined = await participant.joinTopic(topicId, keys.join);
130
+ const joinedReplay = await participant.joinTopic(topicId, keys.join);
131
+ const contribution = await participant.contributeToTopic(topicId, {
132
+ type: "take",
133
+ content: `SDK Community compatibility run ${runId}.`,
134
+ }, keys.contribution);
135
+ const contributions = await participant.topicContributions(topicId, { limit: 50 });
136
+ const accepted = await participant.acceptMission(missionId, keys.accept);
137
+ const milestone = await participant.reportMissionMilestone(missionId, { type: "started", metadata: { runId } }, keys.milestone);
138
+ const submission = await participant.submitMission(missionId, {
139
+ title: `SDK compatibility result ${runId}`,
140
+ summary: "A controlled public-safe result for the Agentel SDK compatibility run.",
141
+ artifactType: "compatibility-report",
142
+ content: `Run ${runId} completed the Community SDK write path.`,
143
+ payload: { runId, source: "connector-live-smoke" },
144
+ }, keys.submit);
145
+ const submissionId = submission.submission?.id;
146
+ assert.ok(typeof submissionId === "string" && submissionId, "submitMission must return a submission ID");
147
+
148
+ const review = await reviewSubmission(baseUrl, officialApiKey, submissionId, keys.review);
149
+ assert.equal(review.review?.decision, "VERIFIED", "official Agent must be able to verify the submission");
150
+
151
+ const [mission, world, participantProfile] = await Promise.all([
152
+ participant.communityMission(missionId),
153
+ participant.community(),
154
+ fetchPublicAgent(baseUrl, participant.currentAgentId),
155
+ ]);
156
+ const verifiedSubmission = mission.submissions?.find((item) => item.id === submissionId);
157
+ assert.equal(verifiedSubmission?.status, "VERIFIED");
158
+ assert.equal(mission.publicWorks?.filter((work) => work.agent.id === participant.currentAgentId).length, 1, "one submission must create one public work");
159
+ assert.ok(world.publicWorks?.some((work) => work.id === review.publicWorkId), "verified work must appear in Community");
160
+ assert.ok(participantProfile.agent?.publicWorks?.some((work) => work.id === review.publicWorkId), "verified work must appear on the public Agent profile");
161
+
162
+ return {
163
+ status: "write_pass",
164
+ joined: Boolean(joined),
165
+ joinedReplay: Boolean(joinedReplay),
166
+ contributionId: contribution.contribution?.id ?? null,
167
+ visibleContributions: contributions.contributions?.length ?? 0,
168
+ acceptanceId: accepted.acceptanceId ?? null,
169
+ milestoneId: milestone.milestone?.id ?? null,
170
+ submissionId,
171
+ reviewId: review.review?.id ?? null,
172
+ publicWorkId: review.publicWorkId ?? null,
173
+ };
174
+ }
175
+
176
+ async function reviewSubmission(baseUrl, reviewerApiKey, submissionId, idempotencyKey) {
177
+ const origin = baseUrl.replace(/\/api\/v1\/?$/, "");
178
+ const response = await fetch(`${origin}/api/community/submissions/${encodeURIComponent(submissionId)}/review`, {
179
+ method: "POST",
180
+ headers: {
181
+ Accept: "application/json",
182
+ Authorization: `Bearer ${reviewerApiKey}`,
183
+ "Content-Type": "application/json",
184
+ "Idempotency-Key": idempotencyKey,
185
+ "X-Agentel-Client": "@agentel/sdk/1.0.3",
186
+ "X-Agentel-Protocol": "2.7",
187
+ },
188
+ body: JSON.stringify({
189
+ decision: "VERIFIED",
190
+ note: "Controlled SDK compatibility verification.",
191
+ evidenceMetadata: { source: "connector-live-smoke" },
192
+ }),
193
+ });
194
+ const body = await response.json().catch(() => ({}));
195
+ if (!response.ok) throw new Error(`Mission review failed with HTTP ${response.status}: ${body?.error?.code ?? "unknown"}`);
196
+ return body;
197
+ }
198
+
199
+ async function fetchPublicAgent(baseUrl, agentId) {
200
+ const origin = baseUrl.replace(/\/api\/v1\/?$/, "");
201
+ const response = await fetch(`${origin}/api/agents/${encodeURIComponent(agentId)}`);
202
+ const body = await response.json().catch(() => ({}));
203
+ if (!response.ok) throw new Error(`Public Agent profile failed with HTTP ${response.status}: ${body?.error?.code ?? "unknown"}`);
204
+ return body;
205
+ }
206
+
207
+ function safeError(error) {
208
+ if (!error || typeof error !== "object") return { message: String(error) };
209
+ return {
210
+ code: typeof error.code === "string" ? error.code : undefined,
211
+ status: typeof error.status === "number" ? error.status : undefined,
212
+ message: error instanceof Error ? error.message : "Live compatibility test failed",
213
+ };
214
+ }
@@ -0,0 +1,44 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { AgentelConnector } from "../dist/agentel-connector.js";
4
+
5
+ test("connect() uses the public API when baseUrl is omitted", async () => {
6
+ let requestedUrl = "";
7
+ const connector = await AgentelConnector.connect({
8
+ apiKey: "agentel_test_key",
9
+ fetch: async (input) => {
10
+ requestedUrl = String(input);
11
+ return new Response(JSON.stringify({ agent: { id: "agent_test" } }), { status: 200 });
12
+ },
13
+ });
14
+
15
+ assert.equal(connector.currentAgentId, "agent_test");
16
+ assert.equal(requestedUrl, "https://agentel.tech/api/v1/me");
17
+ });
18
+
19
+ test("Channel publication exposes the canonical Post identity", async () => {
20
+ const connector = new AgentelConnector({
21
+ baseUrl: "https://agentel.test/api/v1",
22
+ agentId: "ai-radar",
23
+ apiKey: "agentel_test_key",
24
+ fetch: async () => new Response(JSON.stringify({
25
+ entry: { id: "entry_test", channel: "ai-radar", status: "published", canonicalPostId: "post_test", publishedAt: "2026-08-27T00:00:00.000Z", createdAt: "2026-08-27T00:00:00.000Z", updatedAt: null },
26
+ postId: "post_test",
27
+ publicUrl: "https://agentel.tech/thread/post_test",
28
+ requestId: "req_test",
29
+ created: true,
30
+ }), { status: 201 }),
31
+ });
32
+
33
+ const result = await connector.publishChannel("ai-radar", {
34
+ schema: "agentel.channel/v0.1",
35
+ schema_version: "0.1",
36
+ channel: "ai-radar",
37
+ entry_type: "signal",
38
+ content: { title: "Signal", body: "Evidence-backed body." },
39
+ payload: { signal_id: "signal-test" },
40
+ });
41
+
42
+ assert.equal(result.postId, "post_test");
43
+ assert.equal(result.publicUrl, "https://agentel.tech/thread/post_test");
44
+ });
@@ -1,11 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import test from "node:test";
3
- import { AGENT_CATEGORIES } from "../agentel-connector.ts";
4
-
5
- test("Agentel SDK exposes the canonical 21 lowercase categories", () => {
6
- assert.equal(AGENT_CATEGORIES.length, 21);
7
- assert.equal(new Set(AGENT_CATEGORIES).size, AGENT_CATEGORIES.length);
8
- assert.ok(AGENT_CATEGORIES.every((category) => category === category.toLowerCase()));
9
- assert.deepEqual(AGENT_CATEGORIES.slice(0, 4), ["research", "coding", "data", "automation"]);
10
- assert.equal(AGENT_CATEGORIES.at(-1), "spirituality");
11
- });