Releases: shubam14dec/Scalable-Notification-System
Release list
@asyncify-hq/cli@0.3.0
Minor Changes
- d4d36e3:
asyncify devnow decides a new tunnel address is live by asking a public DNS
server (1.1.1.1, falling back to 8.8.8.8) and calling/healthstraight at that
IP, instead of trusting your own machine's DNS — which on some networks lags
minutes behind and made the check fail on tunnels the rest of the internet could
already reach. It waits up to five minutes (was one), prints a line whenever
something changes, takes a new--wait <seconds>flag (or$ASYNCIFY_WAIT), and
tells you when webhooks are live but your own machine still cannot look the
address up. If no public resolver is reachable, it falls back to the previous
behaviour and says so.
@asyncify-hq/node@0.8.7
Patch Changes
-
07eb07c: Agents gain a kill-switch and a file format.
agents.pause(id)stops an agent answering right now without shutting the door on your customers: messages still land in the conversation exactly as they were sent, nothing is refused, and the transcript stays true — what stops is the answering (no model calls, no tools, no calls to your own handler). Each customer is told once that a person is taking over and their conversation moves to your team's human queue, because during an incident filling that queue is the point, not hiding it behind an error.agents.resume(id)puts the agent back on new messages while leaving threads a teammate already picked up with that teammate until they hand back. This is deliberately notupdate(id, { status: 'disabled' })—disabledis a hard off that refuses messages at the door, a pause keeps taking them, and the two are separate axes that can both be true at once, which is whyAgentgainspausedAt(a timestamp, so a post-mortem can read when) besidestatusrather than a third value inside it. Both calls are idempotent, since a double-pressed emergency button must never error, and neither mints a prompt version because a pause changes no config.agents.export(id)returns the whole agent as one JSON file — prompt, model, all six knobs, the budget and backstop numbers, tools with their guards, knowledge as references and workflow keys as requirements — and never a secret of any kind, by construction, so it is safe to commit and read in a diff. The body is the file, with no envelope, so whatexportreturns goes straight back intoagents.importPreview(file)andagents.import(file, { llmApiKey? }). Importing is two steps because "what changes?" is a real question and a green checkmark is not an answer to it: the preview is field-level (changes,toolChanges,mode: 'create' | 'update',missingWorkflows,missingKnowledge,needsLlmKey) and it validates everything the apply validates — through the same schemas the ordinary save routes use — so "preview clean, apply 400" cannot happen. An import deletes nothing: a knob the file does not mention is left as it is, and a tool on the agent but absent from the file is kept, which is whatremovalPolicy: 'kept'states out loud so no client can rendertoolChanges.removedas a threat — an import must not destroy what the file's author never knew about. Applying an update rides the normal save path, so a changed prompt or model mints a version like any other save;status,pausedAt, the canary and version history are never touched. Because keys never travel, creating a managed agent from a file takes the target environment's own key asllmApiKey, a file'sllmBaseUrlonly takes effect alongside a fresh key (so a config file can never repoint a stored credential at another host), and each newly created tool's signing secret is returned exactly once intools.created. New types:AgentConfigFile,AgentConfigTool,AgentConfigKnowledge,AgentConfigChange,AgentConfigToolChanges,AgentImportPreview,AgentImportResult.
@asyncify-hq/node@0.8.6
Patch Changes
- 5d5632a: Per-customer message limits are now typed on the client.
agents.update(id, { subscriberRate: { maxMessages, windowMinutes, notice } })caps how many messages ONE end user may send an agent inside a fixed window; past the cap that person stops getting replies until the window ends and receivesnoticeonce — never on every message, because a limit that answered a flood would be an amplifier. Their messages still land in the conversation exactly as they sent them, so the transcript stays true; only the turn is skipped, and a blocked message costs no model call at all. Everyone else is unaffected, which is the difference from the daily token budget: that one goes quiet for every customer at once, so leaning on it against a single abusive user lets that user mute the agent for everybody. This is the one agent config that works on BOTH runtimes — it is ingress protection, not brain config, and a flood costs a bridge agent its own compute just as surely as it costs a managed agent tokens.subscriberRate: nullswitches it off and forgets the config,Agentgains the field the API now returns, and a config outside the bounds (maxMessages1–1000,windowMinutes1–1440,notice1–2000) is rejected on save and read as OFF rather than clamped, since a limiter throttling at a number you never chose is worse than no limiter. There is deliberately nocandidateoverride on an eval run: a scenario is a burst of messages from one synthetic subscriber by construction, so a gradeable message limit would throttle the check itself.
@asyncify-hq/node@0.8.5
Patch Changes
- 9bc828d: The two per-agent guardrails are now typed on the client.
agents.update(id, { topics: { deny, allow, redirect } })decides what a managed agent will discuss: one small classifier call runs in front of the brain, names what the customer's message is about, and a message on a denied topic — or, whenallowis non-empty, outside it — getsredirectback word for word without the brain ever running.denybeatsallow, and a classifier that errors SKIPS the gate rather than blocking, since fail-closed would mute an agent's whole traffic behind one canned sentence.agents.update(id, { moderation: { denyPhrases, blockPii, fallback } })decides what may ship: every drafted reply is checked in-process — no model, no extra call, no added latency — against case-insensitive substring phrases and, withblockPii, against emails and phone numbers that are not the customer's own; a blocked reply is replaced byfallbackwith any buttons suppressed. It matches typed phrases, not paraphrases, which is the honest price of a check that cannot be down; and the fallback deserves care, because the turn's tools have already run by the time it ships — "a teammate will follow up" is safe where "I couldn't help" can be a lie.nullswitches either off and forgets its config,Agentgains both fields the API now returns, andEvalRunCandidategains them too — with the deliberate asymmetry that omitting them leaves the AGENT'S gates in force for the run (a check grades the agent you have, boundaries included), sonullis how you ask whether the evals still pass with a gate off. Managed agents only: a bridge agent answers 400.
@asyncify-hq/node@0.8.4
Patch Changes
- 71b5802: Cheap-first model routing is now typed on the client.
agents.update(id, { routing: { enabled, cheapModel } })puts a managed agent's simple replies on a smaller model; the moment a routed turn reaches for a consequential tool — a workflow, one of your own tools, a knowledge lookup — the whole turn is discarded and re-run on the agent's main model, so a small model is trusted to talk and never to act.cheapModelmust be an id your own LLM endpoint serves (routing rides the agent's key and base URL) and has no default; a wrong id is safe, since the failed cheap call just escalates.routing: nullswitches it off and forgets the config, andAgentgains theroutingfield the API now returns.EvalRunCandidategainsroutingtoo, so a pre-save check can grade the router the operator is about to turn on — the run executes through it rather than merely recording it, and an explicitnullgrades the agent with routing off. Managed agents only: a bridge agent answers 400.
@asyncify-hq/node@0.8.3
Patch Changes
- 2a2b607: Prompt versioning and canary trials are now typed on the client.
agents.versions.list / get / restorereads a managed agent's append-only prompt history — restore is a save, not a rewind: it publishes an old snapshot as a NEW version and reports both numbers.agents.canary.start / stop / promote / reporttrials a version on a percentage of real conversations (sticky per conversation) and returns the per-arm comparison: counters for both arms plus judged averages sampled from each at the same rate, asCanaryReport/CanaryArmReport.Agentgains thepromptVersionandcanaryfields the API has been sending. Managed agents only; a bridge agent answers 400, and a second concurrent trial is a 409. - 620a8d5: README now documents the
agents.evalssurface (present since 0.5.0 but previously undocumented): creating scenarios, running them, pre-save candidate runs, and readingjudged[]verdicts — with the skipped-is-not-a-pass rule.
@asyncify-hq/node@0.8.2
Patch Changes
- aa68816: Eval runs can now grade an unsaved config.
agents.evals.run()takes an optionalcandidate: { systemPrompt?, model? }— the run uses the real agent, tools, guardrails and knowledge with only the prompt and/or model swapped in, so you can check an edit before it is saved (what the dashboard's pre-save check does). Managed agents only; a candidate on a bridge agent is a 400. The agent is never written to, and a run started with a candidate carries it back onAgentEvalRun.candidate. Types only, and additive: a run without a candidate returns exactly the shape it did before.
@asyncify-hq/node@0.8.1
Patch Changes
- 340576a: Eval run results now include optional
judged[]LLM-judge verdicts. A scenario whoseexpectblocks usejudge(groundedness / tone / refusal) carries oneJudgeVerdictRecordper graded dimension —{ turn, dim, verdict, score?, rationale }— alongside the existingfailures.verdict: 'skipped'marks a dimension no judge client was available for, which is neither a pass nor a failure. Types only, and additive: a scenario that uses no judge returns exactly the shape it did before.
@asyncify-hq/react@0.8.0
Minor Changes
- 34479ca: AgentChat renders a quiet "«name» · team" sender label above replies from a human teammate during a handoff, on both the live WebSocket and fetched-history paths. The conversation status type widens to the
waiting_human/humanhandoff states (exported asConversationStatus): the message input stays enabled so the customer keeps typing — now to the person — while the agent typing indicator is suppressed.
@asyncify-hq/node@0.8.0
Minor Changes
- 34479ca:
conversationsstatus types widen to include thewaiting_humanandhumanhandoff states — onConversationSummary, theliststatus filter, and thegetresponse — so consumers can filter for and read conversations a human teammate is currently handling.