@nuanu-ai/agentify 0.3.1 → 0.5.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/README.md CHANGED
@@ -125,11 +125,12 @@ than the engineer.
125
125
 
126
126
  ## Versions
127
127
 
128
- This is an early `0.x` pilot surface and the names in it can still change. What
129
- does not change quietly is the wire: the SDK and the gateway agree on a
130
- contract version, and a worker whose version the gateway does not share stops
131
- before it takes an order rather than half reading a document and misreporting
132
- its own successes. Register `agentify.on('problem', …)`, which is where you
128
+ This is an early `0.x` pilot surface and the names in it can still change. The
129
+ SDK and the gateway agree on a contract version, and a worker whose version the
130
+ gateway does not share stops before it takes an order. During the pilot that
131
+ version does not move with every change, so a release older than the gateway
132
+ can read a word it does not know as a failure rather than stop: keep the
133
+ package current. Register `agentify.on('problem', …)`, which is where you
133
134
  hear about that and about everything else that did not get through.
134
135
 
135
136
  ## License
package/dist/client.d.ts CHANGED
@@ -14,7 +14,7 @@
14
14
  * calls that answer it, and that is the part worth reading before changing
15
15
  * anything here, because two things that look alike are deliberately not.
16
16
  *
17
- * `order.delivered(...)`, `order.refused(...)` and `order.accepted(...)`
17
+ * `order.delivered(...)`, `order.refused(...)` and `order.accepted()`
18
18
  * build the answer and send nothing; the handler still returns it. A bot
19
19
  * library lets a handler reply and forget, and forgetting there costs a
20
20
  * message; forgetting here is an order nobody answered — redelivered, a
@@ -55,7 +55,7 @@
55
55
  * branch, under a code this package produces and the gateway never sends. The
56
56
  * three codes and what separates them are written down beside them below.
57
57
  */
58
- import type { Acceptance, CardInput, Delivery, HandlerAnswer, Money, Order, OrderAcceptResponse, OrderCallResponse, OrderWithStatus, PublishResult, QuoteRequest, QuoteResponse, Refusal } from "@nuanu-ai/agentify-contracts";
58
+ import type { CardInput, Delivery, HandlerAnswer, Money, Order, OrderAcceptResponse, OrderCallResponse, OrderWithStatus, PublishResult, QuoteRequest, QuoteResponse, Refusal } from "@nuanu-ai/agentify-contracts";
59
59
  import { type EventHandler, type ProblemReporter, type StreamHandlerKind } from "./worker.js";
60
60
  export interface ClientOptions {
61
61
  /**
@@ -112,11 +112,11 @@ export interface OrderCalls {
112
112
  /** A final "this cannot be delivered", as the handler's answer. */
113
113
  refused(refusal: Refusal): HandlerAnswer;
114
114
  /**
115
- * Taking the order on without delivering yet, as the handler's answer. An
116
- * empty acceptance is a complete answer; the expected time is said where it
117
- * is known.
115
+ * Taking the order on without delivering yet, as the handler's answer. It
116
+ * carries nothing: what the merchant is held to is the delivery deadline on
117
+ * the card.
118
118
  */
119
- accepted(acceptance?: Acceptance): HandlerAnswer;
119
+ accepted(): HandlerAnswer;
120
120
  /**
121
121
  * The goods for an order taken on earlier. Idempotent by the order's own
122
122
  * identifier, so repeating it after a dropped connection does no work twice.
@@ -125,7 +125,7 @@ export interface OrderCalls {
125
125
  /** A final "this cannot be delivered" for an order already taken on. */
126
126
  refuse(refusal: Refusal): Promise<OrderCallResponse>;
127
127
  /** Takes the order on from outside a handler. Repeats are ordinary. */
128
- accept(acceptance?: Acceptance): Promise<OrderAcceptResponse>;
128
+ accept(): Promise<OrderAcceptResponse>;
129
129
  }
130
130
  /**
131
131
  * The least an order can be and still be answerable: its identifier, and the
@@ -295,8 +295,11 @@ export interface AgentifyClient {
295
295
  * safe to call on a client that was never started.
296
296
  *
297
297
  * Two things are abandoned rather than finished, and they are not abandoned
298
- * on the same terms. A poll parked at the gateway is dropped, and whatever
299
- * it would have carried was never handed to anybody, so it is redelivered.
298
+ * on the same terms. A poll parked at the gateway is dropped, and the
299
+ * gateway stops it and keeps whatever arrives for the next poll; only
300
+ * something taken in the instant before the drop is lost, and that is an
301
+ * order, which comes again, or an event, which is sent once — the list of
302
+ * open orders is what finds a refund owed whose event went missing.
300
303
  * An answer already on its way — a delivery the handler produced a moment
301
304
  * ago — is dropped too, and there the honest thing to say is that nobody on
302
305
  * this side knows whether it arrived first: the order may already be closed
package/dist/client.js CHANGED
@@ -14,7 +14,7 @@
14
14
  * calls that answer it, and that is the part worth reading before changing
15
15
  * anything here, because two things that look alike are deliberately not.
16
16
  *
17
- * `order.delivered(...)`, `order.refused(...)` and `order.accepted(...)`
17
+ * `order.delivered(...)`, `order.refused(...)` and `order.accepted()`
18
18
  * build the answer and send nothing; the handler still returns it. A bot
19
19
  * library lets a handler reply and forget, and forgetting there costs a
20
20
  * message; forgetting here is an order nobody answered — redelivered, a
@@ -241,8 +241,7 @@ const reportToConsole = (problem) => {
241
241
  * side means changing both in the same commit.
242
242
  *
243
243
  * A key matching neither prefix says nothing at all, which is the third case
244
- * ADR-0020 names: a key issued before the prefix existed, or one that is not
245
- * ours. There is nothing to fall back to. Guessing is wrong in both directions
244
+ * ADR-0020 names: a key naming no environment, or one that is not ours. There is nothing to fall back to. Guessing is wrong in both directions
246
245
  * — telling a merchant their money is play when it is real, or the other way
247
246
  * round — and no line at all is better than a wrong claim about whose money is
248
247
  * at stake.
@@ -360,11 +359,11 @@ export const createClient = (options) => {
360
359
  ? answer.document
361
360
  : { ok: false, error: failedCall(route === "deliver_order", answer.failure) };
362
361
  };
363
- const acceptCall = async (orderId, acceptance) => {
362
+ const acceptCall = async (orderId) => {
364
363
  reachable();
365
364
  const answer = await callRoute(gateway, "accept_order", {
366
365
  path: { order_id: orderId },
367
- body: acceptance ?? {},
366
+ body: {},
368
367
  });
369
368
  // Accepting the same order again is ordinary: an order is taken on afresh
370
369
  // on every redelivery, so a repeat does no work twice.
@@ -384,10 +383,10 @@ export const createClient = (options) => {
384
383
  const callsFor = (orderId) => ({
385
384
  delivered: (delivery) => ({ delivered: delivery }),
386
385
  refused: (refusal) => ({ refused: refusal }),
387
- accepted: (acceptance) => ({ accepted: acceptance ?? {} }),
386
+ accepted: () => ({ accepted: {} }),
388
387
  deliver: (delivery) => orderCall("deliver_order", orderId, delivery),
389
388
  refuse: (refusal) => orderCall("refuse_order", orderId, refusal),
390
- accept: (acceptance) => acceptCall(orderId, acceptance),
389
+ accept: () => acceptCall(orderId),
391
390
  });
392
391
  const withCalls = (order) => Object.assign({}, order, callsFor(order.id));
393
392
  const askedWithAnswers = (question) => Object.assign({}, question, {
package/dist/index.d.ts CHANGED
@@ -32,7 +32,7 @@
32
32
  * here from the contracts package, so that integration code has one import and
33
33
  * not two.
34
34
  */
35
- export type { Acceptance, CallError, Card, CardInput, Delivery, Fulfillment, HandlerAnswer, MerchantFinding, Money, Order, OrderCallResponse, OrderCallResult, OrderEvent, OrderList, OrderStatus, OrderWithStatus, Problem, PublishResult, QuotePurpose, QuoteRequest, QuoteResponse, Refusal, RefusalCode, SalePrice, } from "@nuanu-ai/agentify-contracts";
35
+ export type { CallError, Card, CardInput, Delivery, Fulfillment, HandlerAnswer, MerchantFinding, Money, Order, OrderCallResponse, OrderCallResult, OrderEvent, OrderList, OrderStatus, OrderWithStatus, Problem, PublishResult, QuotePurpose, QuoteRequest, QuoteResponse, Refusal, RefusalCode, SalePrice, Shipment, ShipTo, } from "@nuanu-ai/agentify-contracts";
36
36
  export { CARD_REJECTED, MERCHANT_FINDINGS, ORDER_EVENT_TYPES, RECOMMENDED_REFUSAL_CODES, } from "@nuanu-ai/agentify-contracts";
37
37
  export type { CardCheck } from "./check-card.js";
38
38
  export { checkCard } from "./check-card.js";
package/dist/verify.d.ts CHANGED
@@ -1,17 +1,16 @@
1
1
  /**
2
- * `agentify verify` — the check a merchant runs on themselves before they
3
- * ask us to look.
4
- *
5
- * The portal describes two checks. Is the card enough for an agent to assemble
6
- * a correct purchase, and does the merchant's handler hold idempotency — that
7
- * is, does a second delivery appear when one and the same order arrives twice.
8
- *
9
- * The first is here in full. The second is not, and this file refuses to
10
- * pretend otherwise: it names what is missing, in the same words anyone can
11
- * check against the route table, and answers with a code of its own that says
12
- * "did not run" rather than "passed" or "failed". The reasons are written out
13
- * in `IDEMPOTENCY_IS_NOT_BUILDABLE` below and are worth reading before anybody
14
- * tries to add the check, because the gap is not in this package.
2
+ * `agentify verify` — the check a merchant runs on their cards before they
3
+ * publish them.
4
+ *
5
+ * It checks one thing, in full: whether a card is enough for an agent to
6
+ * assemble a correct purchase, read the way publication reads it. Whether the
7
+ * merchant's handler holds against one order delivered twice is not something
8
+ * a command holding only card files can try — no route raises an order for a
9
+ * merchant's own card — so it is not one of this command's checks, and the
10
+ * output says so and where it is proved instead: with a test purchase against
11
+ * the merchant's own delivery system. Counted here as a check that "did not
12
+ * run", it made every complete card answer as something short of success, and
13
+ * a build reading the exit code took a good card for a failure.
15
14
  *
16
15
  * Which cards are checked is asked for rather than discovered. Nothing in this
17
16
  * package, in the contract or in any decision says where a merchant keeps
@@ -19,56 +18,15 @@
19
18
  * convention — a file name, a directory, a manifest — that nobody agreed to
20
19
  * and that every merchant would then have to work around.
21
20
  */
22
- /**
23
- * What the command answers with.
24
- *
25
- * Four and not two, because a build that branches on this needs to tell a
26
- * check that failed from a check that never ran. Collapsing the two would make
27
- * a missing check look like a passing one on the day somebody decides that
28
- * anything non-zero is a failure and anything else is fine.
29
- */
21
+ /** What the command answers with. */
30
22
  export declare const VERIFY_EXIT: Readonly<{
31
- /** Every check ran and every check passed. */
23
+ /** Every card it was given is complete as far as the contract can tell. */
32
24
  PASSED: 0;
33
- /** A check ran and found something. */
25
+ /** A card has findings. */
34
26
  PROBLEMS: 1;
35
27
  /** The command was called with something it cannot work from. */
36
28
  USAGE: 2;
37
- /** A check could not be run at all, so nothing is claimed about it. */
38
- COULD_NOT_RUN: 3;
39
29
  }>;
40
- /**
41
- * Why the idempotency run is not in this version, stated so it can be checked
42
- * rather than taken on trust.
43
- *
44
- * The run the portal describes needs a test order to exist: the same order
45
- * delivered twice through the live subscription, marked `test`, against a card
46
- * that is published but not yet in any catalog. Four things it needs are
47
- * absent, and none of them is ours to add here.
48
- *
49
- * There is no route that asks for one. The only way an order comes into being
50
- * on this surface is `purchase_item`, and that is the payment exchange itself
51
- * — an agent buying — not a request a merchant can make about their own card.
52
- *
53
- * `PurchaseRequestSchema` is closed and carries the purchase parameters and
54
- * nothing else, so there is nowhere in a purchase to say "this one is a test".
55
- *
56
- * Nothing says how an order's `test` flag comes to be true. The order carries
57
- * it, the handler is told to branch on it, and no document in the contract
58
- * describes who sets it or from what.
59
- *
60
- * And a card carries no marker for "published, not yet in catalogs", which is
61
- * the state the portal says these orders are raised against.
62
- *
63
- * What is missing is not an environment to run it in. There are two sites, the
64
- * test one settles against test funds, and a key says which it belongs to
65
- * (ADR-0020) — so a merchant already has somewhere to try their integration
66
- * without spending anything. What is missing is a way to make an order happen
67
- * there: no route raises one, and the four absences above are the whole of what
68
- * would have to exist first. Inventing a route or a field here would be
69
- * designing that, and this is not the place where it is designed.
70
- */
71
- export declare const IDEMPOTENCY_IS_NOT_BUILDABLE: string;
72
30
  /** The code a finding carries when the file held no card to check at all. */
73
31
  export declare const NOT_JSON = "not_json";
74
32
  export type Say = (line: string) => void;
package/dist/verify.js CHANGED
@@ -1,17 +1,16 @@
1
1
  /**
2
- * `agentify verify` — the check a merchant runs on themselves before they
3
- * ask us to look.
2
+ * `agentify verify` — the check a merchant runs on their cards before they
3
+ * publish them.
4
4
  *
5
- * The portal describes two checks. Is the card enough for an agent to assemble
6
- * a correct purchase, and does the merchant's handler hold idempotency — that
7
- * is, does a second delivery appear when one and the same order arrives twice.
8
- *
9
- * The first is here in full. The second is not, and this file refuses to
10
- * pretend otherwise: it names what is missing, in the same words anyone can
11
- * check against the route table, and answers with a code of its own that says
12
- * "did not run" rather than "passed" or "failed". The reasons are written out
13
- * in `IDEMPOTENCY_IS_NOT_BUILDABLE` below and are worth reading before anybody
14
- * tries to add the check, because the gap is not in this package.
5
+ * It checks one thing, in full: whether a card is enough for an agent to
6
+ * assemble a correct purchase, read the way publication reads it. Whether the
7
+ * merchant's handler holds against one order delivered twice is not something
8
+ * a command holding only card files can try — no route raises an order for a
9
+ * merchant's own card — so it is not one of this command's checks, and the
10
+ * output says so and where it is proved instead: with a test purchase against
11
+ * the merchant's own delivery system. Counted here as a check that "did not
12
+ * run", it made every complete card answer as something short of success, and
13
+ * a build reading the exit code took a good card for a failure.
15
14
  *
16
15
  * Which cards are checked is asked for rather than discovered. Nothing in this
17
16
  * package, in the contract or in any decision says where a merchant keeps
@@ -23,68 +22,21 @@ import { readFileSync } from "node:fs";
23
22
  import { basename } from "node:path";
24
23
  import { checkCard } from "./check-card.js";
25
24
  import { describeProblems } from "./schema.js";
26
- /**
27
- * What the command answers with.
28
- *
29
- * Four and not two, because a build that branches on this needs to tell a
30
- * check that failed from a check that never ran. Collapsing the two would make
31
- * a missing check look like a passing one on the day somebody decides that
32
- * anything non-zero is a failure and anything else is fine.
33
- */
25
+ /** What the command answers with. */
34
26
  export const VERIFY_EXIT = Object.freeze({
35
- /** Every check ran and every check passed. */
27
+ /** Every card it was given is complete as far as the contract can tell. */
36
28
  PASSED: 0,
37
- /** A check ran and found something. */
29
+ /** A card has findings. */
38
30
  PROBLEMS: 1,
39
31
  /** The command was called with something it cannot work from. */
40
32
  USAGE: 2,
41
- /** A check could not be run at all, so nothing is claimed about it. */
42
- COULD_NOT_RUN: 3,
43
33
  });
44
- /**
45
- * Why the idempotency run is not in this version, stated so it can be checked
46
- * rather than taken on trust.
47
- *
48
- * The run the portal describes needs a test order to exist: the same order
49
- * delivered twice through the live subscription, marked `test`, against a card
50
- * that is published but not yet in any catalog. Four things it needs are
51
- * absent, and none of them is ours to add here.
52
- *
53
- * There is no route that asks for one. The only way an order comes into being
54
- * on this surface is `purchase_item`, and that is the payment exchange itself
55
- * — an agent buying — not a request a merchant can make about their own card.
56
- *
57
- * `PurchaseRequestSchema` is closed and carries the purchase parameters and
58
- * nothing else, so there is nowhere in a purchase to say "this one is a test".
59
- *
60
- * Nothing says how an order's `test` flag comes to be true. The order carries
61
- * it, the handler is told to branch on it, and no document in the contract
62
- * describes who sets it or from what.
63
- *
64
- * And a card carries no marker for "published, not yet in catalogs", which is
65
- * the state the portal says these orders are raised against.
66
- *
67
- * What is missing is not an environment to run it in. There are two sites, the
68
- * test one settles against test funds, and a key says which it belongs to
69
- * (ADR-0020) — so a merchant already has somewhere to try their integration
70
- * without spending anything. What is missing is a way to make an order happen
71
- * there: no route raises one, and the four absences above are the whole of what
72
- * would have to exist first. Inventing a route or a field here would be
73
- * designing that, and this is not the place where it is designed.
74
- */
75
- export const IDEMPOTENCY_IS_NOT_BUILDABLE = [
76
- "The idempotency run needs a test order, and nothing on the surface can ask for one:",
77
- " - no route raises an order for a merchant's own card; the only way an order",
78
- " comes into being is purchase_item, which is an agent's payment exchange",
79
- " - purchase_item's body carries the purchase parameters and nothing else, so",
80
- " there is nowhere in it to say that a purchase is a test",
81
- " - nothing in the contract says how an order's test flag comes to be true",
82
- " - a card carries no marker for published-but-not-yet-in-catalogs, which is",
83
- " the state the documentation raises these orders against",
84
- "The test site is not what is missing: it exists, it settles against test",
85
- "funds, and a csk_test_ key belongs to it. What is missing is a way to make",
86
- "an order happen there, and inventing a route or a field here would be",
87
- "designing one.",
34
+ /** What this command does not check, and where that is proved instead. */
35
+ const NOT_CHECKED_HERE = [
36
+ "Repeats of one order: not checked by this command.",
37
+ " Whether a second delivery of the same order makes second goods is proved",
38
+ " against your own delivery system, with a test purchase: the buyer has to",
39
+ " keep what the first delivery carried.",
88
40
  ].join("\n");
89
41
  /** The code a finding carries when the file held no card to check at all. */
90
42
  export const NOT_JSON = "not_json";
@@ -99,10 +51,9 @@ const USAGE = [
99
51
  "",
100
52
  "Checks each card against the published contract before it is published.",
101
53
  "",
102
- "Answers: 1 a check found something, 2 called with something it cannot work",
103
- "from, 3 a check could not be run. Zero, which means every check passed, is",
104
- "not reachable today: the idempotency run cannot be built, and this command",
105
- "will not report success for a check that never happened.",
54
+ "Answers: 0 every card is complete as far as the contract can tell, 1 a card",
55
+ "has findings, 2 called with something it cannot work from. Whether your",
56
+ "handler holds against an order delivered twice is not checked here.",
106
57
  ].join("\n");
107
58
  /**
108
59
  * Why the bare command the documentation shows does not run.
@@ -138,8 +89,8 @@ const USAGE = [
138
89
  * dry run of publishing, which can refuse for reasons no schema carries; there
139
90
  * is no such route (`docs/research/00-open-questions.md`).
140
91
  *
141
- * So this is a stop and not a scolding, and it is answered with the code that
142
- * means "did not run". The way through it is to name the card files, which are
92
+ * So this is a stop and not a scolding, and it is answered as a call the
93
+ * command cannot work from. The way through it is to name the card files, which are
143
94
  * the copy the merchant can still change — the file is what the next publish
144
95
  * carries, whether the card is new or an edit to one already out.
145
96
  */
@@ -195,7 +146,7 @@ export const runVerify = async (argv, say) => {
195
146
  say(NOTHING_TO_CHECK);
196
147
  say("");
197
148
  say(USAGE);
198
- return VERIFY_EXIT.COULD_NOT_RUN;
149
+ return VERIFY_EXIT.USAGE;
199
150
  }
200
151
  const checked = [];
201
152
  for (const file of files) {
@@ -225,12 +176,10 @@ export const runVerify = async (argv, say) => {
225
176
  say(" Fix these and run again: a card whose shape is wrong is not checked against the rules that compare one field with another, so there may be more behind them.");
226
177
  }
227
178
  say("");
228
- say("Idempotency");
229
- say(" could not be run.");
230
- say(IDEMPOTENCY_IS_NOT_BUILDABLE);
179
+ say(NOT_CHECKED_HERE);
231
180
  say("");
232
181
  say(faulted.length > 0
233
- ? "Verdict: the cards have findings, and the idempotency run did not happen."
234
- : "Verdict: the cards are complete. Nothing is claimed about idempotency — that check did not run.");
235
- return faulted.length > 0 ? VERIFY_EXIT.PROBLEMS : VERIFY_EXIT.COULD_NOT_RUN;
182
+ ? "Verdict: the cards have findings."
183
+ : "Verdict: the cards are complete as far as the contract can tell.");
184
+ return faulted.length > 0 ? VERIFY_EXIT.PROBLEMS : VERIFY_EXIT.PASSED;
236
185
  };
package/dist/worker.d.ts CHANGED
@@ -320,8 +320,11 @@ export interface RunningWorker {
320
320
  * Stops the loop and waits for it to finish. Safe to call twice.
321
321
  *
322
322
  * Two things are abandoned rather than finished, and they are not abandoned
323
- * on the same terms. A poll parked at the gateway is dropped, and whatever
324
- * it would have carried was never handed to anybody, so it is redelivered.
323
+ * on the same terms. A poll parked at the gateway is dropped, and the
324
+ * gateway stops it and keeps whatever arrives for the next poll; only
325
+ * something taken in the instant before the drop is lost, and that is an
326
+ * order, which comes again, or an event, which is sent once — the list of
327
+ * open orders is what finds a refund owed whose event went missing.
325
328
  * An answer already on its way — a delivery the handler produced a moment
326
329
  * ago — is dropped too, and there the honest thing to say is that nobody on
327
330
  * this side knows whether it arrived first: the order may already be closed
package/dist/worker.js CHANGED
@@ -67,7 +67,7 @@ import { callRoute, REACH, whatIsKnown } from "./transport.js";
67
67
  * order that never comes.
68
68
  */
69
69
  const andSoTheOrder = (failure) => failure.reach === REACH.NOT_RECEIVED
70
- ? ", so the order will be delivered again"
70
+ ? ", so the order counts as undelivered: it is sent again while it has attempts left and its deadline allows, and otherwise ends undelivered"
71
71
  : ", so whether the order is delivered again is not something this side can say";
72
72
  /**
73
73
  * The things the worker has to tell the merchant about, under the names their
@@ -361,7 +361,7 @@ deadlineMs = POLL_DEADLINE_MS) => {
361
361
  kind: WORKER_PROBLEM_KINDS.NO_HANDLER,
362
362
  fatal: false,
363
363
  subject: order.id,
364
- message: `order ${order.id} arrived and no handler was registered with on('${REGISTERED_AS.order}'), so it was left unanswered and will be delivered again`,
364
+ message: `order ${order.id} arrived and no handler was registered with on('${REGISTERED_AS.order}'), so it was left unanswered: it is sent again while it has attempts left and its deadline allows, and otherwise ends undelivered`,
365
365
  });
366
366
  return;
367
367
  }
@@ -375,7 +375,7 @@ deadlineMs = POLL_DEADLINE_MS) => {
375
375
  fatal: false,
376
376
  cause,
377
377
  subject: order.id,
378
- message: `the handler threw on order ${order.id}, so nothing was answered and the order will be delivered again: ${String(cause)}`,
378
+ message: `the handler threw on order ${order.id}, so nothing was answered and the order counts as undelivered — it is sent again while it has attempts left and its deadline allows, and otherwise ends undelivered: ${String(cause)}`,
379
379
  });
380
380
  return;
381
381
  }
@@ -385,7 +385,7 @@ deadlineMs = POLL_DEADLINE_MS) => {
385
385
  kind: WORKER_PROBLEM_KINDS.HANDLER_ANSWER_REFUSED,
386
386
  fatal: false,
387
387
  subject: order.id,
388
- message: `the handler's answer for order ${order.id} is not one the contract carries, so nothing was sent and the order will be delivered again:\n${describeProblems(problemsOf(checked.error.issues))}`,
388
+ message: `the handler's answer for order ${order.id} is not one the contract carries, so nothing was sent and the order counts as undelivered — it is sent again while it has attempts left and its deadline allows, and otherwise ends undelivered:\n${describeProblems(problemsOf(checked.error.issues))}`,
389
389
  });
390
390
  return;
391
391
  }
@@ -550,11 +550,20 @@ deadlineMs = POLL_DEADLINE_MS) => {
550
550
  const remark = spoken !== undefined && !speaksContract(spoken)
551
551
  ? ` — what answered names contract version ${spoken} while this SDK speaks ${contractVersion}, which may mean the gateway is of another version`
552
552
  : "";
553
+ // A refusal that says waiting will not change it — a key revoked or
554
+ // never accepted is the case that matters — is still not taken as the
555
+ // end: one such answer from a proxy must not stop a merchant's worker
556
+ // for good. But it is said for what it is, because "asking again"
557
+ // alone reads as something that will pass, and a revoked key takes no
558
+ // new orders until it is replaced.
559
+ const settled = answer.failure.refusal?.retryable === false;
553
560
  failures += 1;
554
561
  report({
555
562
  kind: WORKER_PROBLEM_KINDS.POLL_FAILED,
556
563
  fatal: false,
557
- message: `${answer.failure.reason}${remark} — asking again after a wait`,
564
+ message: settled
565
+ ? `${answer.failure.reason}${remark} — the gateway says asking again will not change this, and no new orders arrive until it is fixed (a revoked key has to be replaced); asking again after a wait in case it was not the gateway that answered`
566
+ : `${answer.failure.reason}${remark} — asking again after a wait`,
558
567
  });
559
568
  await rest(retryDelayMs(failures, clock.random()));
560
569
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuanu-ai/agentify",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Sell your existing products to AI agents: publish product cards to an x402 storefront and fill the paid orders from your own service.",
6
6
  "keywords": [
@@ -36,7 +36,7 @@
36
36
  "access": "public"
37
37
  },
38
38
  "dependencies": {
39
- "@nuanu-ai/agentify-contracts": "0.7.0"
39
+ "@nuanu-ai/agentify-contracts": "0.9.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "vitest": "^4.1.11",