@ambasada/village 0.8.2 → 0.8.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/dist/cli.mjs +139 -44
- package/dist/client.d.ts +19 -2
- package/dist/index.js +63 -14
- package/dist/types.d.ts +39 -0
- package/package.json +3 -3
package/dist/client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Ambasada, type AmbasadaOptions } from "@ambasada/sdk/platform";
|
|
2
2
|
import { type IncomingRequest } from "./receipt.js";
|
|
3
|
-
import type { AgentConfigInput, BuyOptions, BuyResult, ChatRequest, ChatResponse, CheckoutInput, CheckoutReceiptClaims, CheckoutSession, ImageRequest, ImageResult, LedgerPage, Listing, ListingInput, MarketOffering, PaidCall, PaymentLink, PaymentLinkInput, ToolListing, ToolResult, TranscriptEntry, TransferOptions, Wallet, WebhookDelivery, WebhookEvent } from "./types.js";
|
|
3
|
+
import type { AgentConfigInput, BuyOptions, BuyResult, ChatRequest, ChatResponse, CheckoutInput, CheckoutReceiptClaims, CheckoutSession, ImageRequest, ImageResult, LedgerPage, Listing, ListingInput, MarketOffering, PaidCall, PaymentLink, PaymentLinkInput, ToolListing, ToolResult, TranscriptEntry, ChatLine, ChatProposal, ChatTurn, TransferOptions, Wallet, WebhookDelivery, WebhookEvent } from "./types.js";
|
|
4
4
|
/** The Hack A Ton API. The platform API (`@ambasada/sdk`) is https://api.ambasada.dev. */
|
|
5
5
|
export declare const DEFAULT_VILLAGE_API_URL = "https://hackaton.ambasada.id";
|
|
6
6
|
export declare const DEFAULT_MODEL = "deepseek-v4.1-flash";
|
|
@@ -136,8 +136,25 @@ export declare class Village extends Ambasada {
|
|
|
136
136
|
config: unknown;
|
|
137
137
|
limits?: unknown;
|
|
138
138
|
}>;
|
|
139
|
-
/** A message the agent answers on its next wake. */
|
|
139
|
+
/** A message from the team: the agent answers in the chat now, and reads it on its next wake. */
|
|
140
140
|
steer: (text: string) => Promise<unknown>;
|
|
141
|
+
/**
|
|
142
|
+
* Talk to your agent (docs/AGENT-CHAT.md). With `wait` (the default) it resolves with the reply, up to a
|
|
143
|
+
* minute; the same `idempotencyKey` is the same turn, so a retry never asks twice.
|
|
144
|
+
*/
|
|
145
|
+
chat: (text: string, options?: {
|
|
146
|
+
idempotencyKey?: string;
|
|
147
|
+
wait?: boolean;
|
|
148
|
+
}) => Promise<ChatTurn>;
|
|
149
|
+
/** The chat, oldest first: your words, the agent's replies and proposals, and cards for what happened. */
|
|
150
|
+
messages: (options?: {
|
|
151
|
+
since?: number;
|
|
152
|
+
limit?: number;
|
|
153
|
+
}) => Promise<ChatLine[]>;
|
|
154
|
+
/** Proposals waiting for your team. */
|
|
155
|
+
proposals: () => Promise<ChatProposal[]>;
|
|
156
|
+
/** Approve (it runs now) or decline a proposal. */
|
|
157
|
+
decide: (id: string, decision: "approve" | "decline", reason?: string) => Promise<unknown>;
|
|
141
158
|
transcript: (since?: number, limit?: number) => Promise<{
|
|
142
159
|
entries: TranscriptEntry[];
|
|
143
160
|
next_since: number;
|
package/dist/index.js
CHANGED
|
@@ -85,6 +85,7 @@ __export(src_exports, {
|
|
|
85
85
|
parseCheckpoint: () => parseCheckpoint,
|
|
86
86
|
parseVerifierKey: () => parseVerifierKey,
|
|
87
87
|
pngInfo: () => pngInfo,
|
|
88
|
+
probeManifest: () => probeManifest,
|
|
88
89
|
publicJwkOf: () => publicJwkOf,
|
|
89
90
|
randomId: () => randomId,
|
|
90
91
|
rootFromLeafHashes: () => rootFromLeafHashes,
|
|
@@ -151,6 +152,8 @@ var ERROR_STATUS = {
|
|
|
151
152
|
BUDGET_EXCEEDED: 402,
|
|
152
153
|
CAP_INBOX_RATE: 429,
|
|
153
154
|
// inbox texts: 20 an hour, 1 unsolicited per counterparty an hour (EXCHANGE.md §8)
|
|
155
|
+
CAP_CHAT_RATE: 429,
|
|
156
|
+
// turns of the CEO chat per company (docs/AGENT-CHAT.md)
|
|
154
157
|
// Additions needed by the gateway and bank
|
|
155
158
|
UPSTREAM_ERROR: 502,
|
|
156
159
|
PRICE_ABOVE_MAX: 409,
|
|
@@ -1266,6 +1269,34 @@ var USER_AGENT = "ambasada-check/1 (+https://ambasada.id)";
|
|
|
1266
1269
|
async function get(fetchImpl, url) {
|
|
1267
1270
|
return fetchImpl(url, { redirect: "manual", signal: AbortSignal.timeout(FETCH_TIMEOUT_MS), headers: { "user-agent": USER_AGENT, accept: "*/*" } });
|
|
1268
1271
|
}
|
|
1272
|
+
async function probeManifest(origin, fetchImpl = fetch) {
|
|
1273
|
+
try {
|
|
1274
|
+
const res = await get(fetchImpl, new URL(MANIFEST_URL_PATH, origin).toString());
|
|
1275
|
+
if (res.status !== 200) {
|
|
1276
|
+
await res.body?.cancel();
|
|
1277
|
+
return { on: false, entry: null };
|
|
1278
|
+
}
|
|
1279
|
+
const m = JSON.parse(await res.text());
|
|
1280
|
+
if (!m || typeof m !== "object" || m.schema !== MANIFEST_SCHEMA && m.schema !== 1) return { on: false, entry: null };
|
|
1281
|
+
return { on: true, entry: typeof m.entry === "string" && m.entry.startsWith("/") ? m.entry : "/" };
|
|
1282
|
+
} catch {
|
|
1283
|
+
return { on: false, entry: null };
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
async function checkPage(fetchImpl, url, report, html) {
|
|
1287
|
+
try {
|
|
1288
|
+
const started = Date.now();
|
|
1289
|
+
const res = await get(fetchImpl, url);
|
|
1290
|
+
const text = res.status === 200 && html ? await res.text() : "";
|
|
1291
|
+
if (!html) await res.body?.cancel();
|
|
1292
|
+
const ms = Date.now() - started;
|
|
1293
|
+
if (res.status !== 200) report.problems.push({ code: "entry_status", message: `${url} answered ${res.status}${res.headers.get("location") ? ` \u2192 ${res.headers.get("location")}` : ""}; ${html ? "the entry" : "the app"} must answer 200 without redirects` });
|
|
1294
|
+
else if (html) merge(report, checkHtml(text));
|
|
1295
|
+
if (ms > SLOW_ENTRY_MS) report.warnings.push({ code: "slow_entry", message: `${url} took ${ms} ms; aim for under ${SLOW_ENTRY_MS} ms${html ? " (the splash stays until ready())" : ""}` });
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
report.problems.push({ code: "entry_fetch", message: `could not fetch ${url}: ${error.message}` });
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1269
1300
|
async function checkRemote(url, fetchImpl = fetch, options = {}) {
|
|
1270
1301
|
const report = { ok: true, target: url, problems: [], warnings: [], notes: [] };
|
|
1271
1302
|
let origin;
|
|
@@ -1279,8 +1310,23 @@ async function checkRemote(url, fetchImpl = fetch, options = {}) {
|
|
|
1279
1310
|
if (origin.protocol !== "https:") report.problems.push({ code: "not_https", message: "the app must be served over https" });
|
|
1280
1311
|
let manifest = null;
|
|
1281
1312
|
const manifestUrl = new URL(MANIFEST_URL_PATH, origin).toString();
|
|
1313
|
+
if (options.miniapp === false) {
|
|
1314
|
+
report.miniapp = { on: false, entry: null };
|
|
1315
|
+
report.notes.push("mini app: off");
|
|
1316
|
+
await checkPage(fetchImpl, new URL("/", origin).toString(), report, false);
|
|
1317
|
+
report.ok = report.problems.length === 0;
|
|
1318
|
+
return report;
|
|
1319
|
+
}
|
|
1282
1320
|
try {
|
|
1283
1321
|
let res = await get(fetchImpl, manifestUrl);
|
|
1322
|
+
if (res.status === 404 && options.miniapp === void 0) {
|
|
1323
|
+
await res.body?.cancel();
|
|
1324
|
+
report.miniapp = { on: false, entry: null };
|
|
1325
|
+
report.notes.push(`mini app: off (no ${MANIFEST_URL_PATH})`);
|
|
1326
|
+
await checkPage(fetchImpl, new URL("/", origin).toString(), report, false);
|
|
1327
|
+
report.ok = report.problems.length === 0;
|
|
1328
|
+
return report;
|
|
1329
|
+
}
|
|
1284
1330
|
if (res.status >= 300 && res.status < 400) {
|
|
1285
1331
|
const location = res.headers.get("location") ?? "";
|
|
1286
1332
|
if (res.status === 307 && HOSTED_MANIFEST.test(location)) {
|
|
@@ -1302,18 +1348,8 @@ async function checkRemote(url, fetchImpl = fetch, options = {}) {
|
|
|
1302
1348
|
report.problems.push({ code: "manifest_fetch", message: `could not fetch ${manifestUrl}: ${error.message}` });
|
|
1303
1349
|
}
|
|
1304
1350
|
const entryPath = typeof manifest?.entry === "string" && manifest.entry.startsWith("/") ? manifest.entry : "/";
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
const started = Date.now();
|
|
1308
|
-
const res = await get(fetchImpl, entryUrl);
|
|
1309
|
-
const html = res.status === 200 ? await res.text() : "";
|
|
1310
|
-
const ms = Date.now() - started;
|
|
1311
|
-
if (res.status !== 200) report.problems.push({ code: "entry_status", message: `${entryUrl} answered ${res.status}${res.headers.get("location") ? ` \u2192 ${res.headers.get("location")}` : ""}; the entry must answer 200 without redirects` });
|
|
1312
|
-
else merge(report, checkHtml(html));
|
|
1313
|
-
if (ms > SLOW_ENTRY_MS) report.warnings.push({ code: "slow_entry", message: `${entryUrl} took ${ms} ms; the splash stays until ready(), aim for under ${SLOW_ENTRY_MS} ms` });
|
|
1314
|
-
} catch (error) {
|
|
1315
|
-
report.problems.push({ code: "entry_fetch", message: `could not fetch ${entryUrl}: ${error.message}` });
|
|
1316
|
-
}
|
|
1351
|
+
report.miniapp = { on: true, entry: entryPath };
|
|
1352
|
+
await checkPage(fetchImpl, new URL(entryPath, origin).toString(), report, true);
|
|
1317
1353
|
if (typeof manifest?.icon === "string" && manifest.icon) {
|
|
1318
1354
|
const iconUrl = new URL(manifest.icon, origin).toString();
|
|
1319
1355
|
try {
|
|
@@ -1381,9 +1417,11 @@ function summarizeCheck(report, now = /* @__PURE__ */ new Date()) {
|
|
|
1381
1417
|
...report.warnings.map((w) => ({ ...w, level: "warning", label: findingLabel(w.code) }))
|
|
1382
1418
|
];
|
|
1383
1419
|
const needs = [...new Set(issues.filter((i) => i.level === "problem").map((i) => i.label))];
|
|
1384
|
-
|
|
1420
|
+
const miniapp = report.miniapp ? { miniapp: report.miniapp.on ? "on" : "off", entry: report.miniapp.entry } : {};
|
|
1421
|
+
return { ok: report.ok, issues, needs, checked_at: now.toISOString(), ...miniapp };
|
|
1385
1422
|
}
|
|
1386
1423
|
function listingLine(tier, check) {
|
|
1424
|
+
if (check?.miniapp === "off") return "Mini app: off";
|
|
1387
1425
|
if (tier === "verified") return "Verified";
|
|
1388
1426
|
if (tier === "listed") return "Listed for the event";
|
|
1389
1427
|
if (tier === "unlisted") return check?.needs.length ? `Unlisted \xB7 needs: ${check.needs.join(", ")}` : "Unlisted";
|
|
@@ -1805,8 +1843,19 @@ var Village = class extends Ambasada {
|
|
|
1805
1843
|
agent = {
|
|
1806
1844
|
/** Replaces the agent's prompt (`agent.md`) and settings (`settings.json`). */
|
|
1807
1845
|
config: (config) => this.call("POST", "/v1/agent/config", { body: config }),
|
|
1808
|
-
/** A message the agent answers on its next wake. */
|
|
1846
|
+
/** A message from the team: the agent answers in the chat now, and reads it on its next wake. */
|
|
1809
1847
|
steer: (text) => this.call("POST", "/v1/agent/steer", { body: { text } }),
|
|
1848
|
+
/**
|
|
1849
|
+
* Talk to your agent (docs/AGENT-CHAT.md). With `wait` (the default) it resolves with the reply, up to a
|
|
1850
|
+
* minute; the same `idempotencyKey` is the same turn, so a retry never asks twice.
|
|
1851
|
+
*/
|
|
1852
|
+
chat: (text, options = {}) => this.call("POST", "/v1/agent/chat/turn", { body: { text, wait: options.wait ?? true, idempotency_key: options.idempotencyKey ?? randomKey() } }),
|
|
1853
|
+
/** The chat, oldest first: your words, the agent's replies and proposals, and cards for what happened. */
|
|
1854
|
+
messages: async (options = {}) => (await this.call("GET", "/v1/agent/chat/messages", { query: { limit: options.limit ?? 50, ...options.since ? { since: options.since } : {} } })).messages,
|
|
1855
|
+
/** Proposals waiting for your team. */
|
|
1856
|
+
proposals: async () => (await this.call("GET", "/v1/agent/chat/proposals")).proposals,
|
|
1857
|
+
/** Approve (it runs now) or decline a proposal. */
|
|
1858
|
+
decide: (id, decision, reason) => this.call("POST", `/v1/agent/chat/proposals/${encodeURIComponent(id)}`, { body: { decision, ...reason ? { reason } : {} } }),
|
|
1810
1859
|
transcript: (since = 0, limit = 200) => this.call("GET", "/v1/agent/transcript", { query: { since, limit } }),
|
|
1811
1860
|
state: () => this.call("GET", "/v1/agent/state")
|
|
1812
1861
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -296,6 +296,45 @@ export interface TranscriptEntry {
|
|
|
296
296
|
data?: unknown;
|
|
297
297
|
[key: string]: unknown;
|
|
298
298
|
}
|
|
299
|
+
/** Something the agent wants to do, waiting for the team in the chat (docs/AGENT-CHAT.md). */
|
|
300
|
+
export interface ChatProposal {
|
|
301
|
+
/** What `agent.decide(id, …)` takes. */
|
|
302
|
+
id: string;
|
|
303
|
+
tool: string;
|
|
304
|
+
/** One plain sentence: "Change the price of translate to 1.2 TON". */
|
|
305
|
+
text: string;
|
|
306
|
+
input: unknown;
|
|
307
|
+
risk?: "low" | "medium" | "high";
|
|
308
|
+
state: "pending" | "approved" | "declined" | "done" | "failed";
|
|
309
|
+
}
|
|
310
|
+
/** One line of the chat with your agent: your words, its reply (with proposals), or a card for what happened. */
|
|
311
|
+
export interface ChatLine {
|
|
312
|
+
id: string;
|
|
313
|
+
role: "user" | "agent" | "card" | "village";
|
|
314
|
+
at: number | null;
|
|
315
|
+
text: string;
|
|
316
|
+
card?: {
|
|
317
|
+
id: string;
|
|
318
|
+
kind: string;
|
|
319
|
+
title: string;
|
|
320
|
+
body?: string | null;
|
|
321
|
+
delta_ton?: number | null;
|
|
322
|
+
from?: string | null;
|
|
323
|
+
at: number;
|
|
324
|
+
};
|
|
325
|
+
proposals?: ChatProposal[];
|
|
326
|
+
cost_ton?: number;
|
|
327
|
+
}
|
|
328
|
+
export interface ChatTurn {
|
|
329
|
+
submission_id: string;
|
|
330
|
+
message_id: string;
|
|
331
|
+
/** false when this idempotency key was already sent: the same turn, not a new one. */
|
|
332
|
+
accepted: boolean;
|
|
333
|
+
/** With `wait`: how the turn ended and the agent's reply. */
|
|
334
|
+
status?: "completed" | "error" | "aborted" | "skipped" | "pending" | "running";
|
|
335
|
+
error?: string;
|
|
336
|
+
reply?: ChatLine | null;
|
|
337
|
+
}
|
|
299
338
|
export interface MarketOffering {
|
|
300
339
|
id: string;
|
|
301
340
|
company?: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ambasada/village",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.4",
|
|
4
4
|
"description": "The Hack A Ton add-on for Ambasada apps: TON checkout, receipts, offerings, LLM, the agent, the market; CLI commands and the local bank.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"node": ">=22"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@ambasada/sdk": "^0.8.
|
|
30
|
+
"@ambasada/sdk": "^0.8.4"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/node": "^22.20.5",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"typescript": "~5.9.3",
|
|
36
36
|
"vitest": "^4.1.12",
|
|
37
37
|
"@ambasada/protocol": "0.1.0",
|
|
38
|
-
"@ambasada/cli": "0.8.
|
|
38
|
+
"@ambasada/cli": "0.8.4"
|
|
39
39
|
},
|
|
40
40
|
"author": "CENTYSO (https://centyso.com)",
|
|
41
41
|
"homepage": "https://ambasada.id",
|