zero-slop 2.10.0 → 2.10.1

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
@@ -30,12 +30,12 @@
30
30
 
31
31
  <p align="center">
32
32
  <a href="https://github.com/manavmishra/ZeroSlop/actions/workflows/validate.yml"><img alt="Validate" src="https://github.com/manavmishra/ZeroSlop/actions/workflows/validate.yml/badge.svg"></a>
33
- <img alt="Version 2.10.0" src="https://img.shields.io/badge/version-2.10.0-72528F?color=C15732">
33
+ <img alt="Version 2.10.1" src="https://img.shields.io/badge/version-2.10.1-72528F?color=C15732">
34
34
  <a href="https://www.npmjs.com/package/zero-slop"><img alt="npm version" src="https://img.shields.io/npm/v/zero-slop?color=C15732"></a>
35
35
  <a href="https://www.npmjs.com/package/zero-slop"><img alt="npm downloads" src="https://img.shields.io/npm/dm/zero-slop?color=17634F"></a>
36
36
  <a href="https://github.com/manavmishra/ZeroSlop/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/manavmishra/ZeroSlop?style=flat&color=C15732"></a>
37
37
  <a href="LICENSE"><img alt="MIT license" src="https://img.shields.io/badge/license-MIT-141412"></a>
38
- <a href="https://hol.org/guard/plugins"><img alt="Listed in the HOL plugin registry" src="https://img.shields.io/badge/HOL%20registry-listed-2C6E8F"></a>
38
+ <a href="https://hol.org/registry/plugins/manav-mishra%2Fzero-slop"><img alt="Listed in the HOL plugin registry" src="https://img.shields.io/badge/HOL%20registry-listed-2C6E8F"></a>
39
39
  </p>
40
40
 
41
41
  ```sh
@@ -136,7 +136,7 @@ https://mcp.zero-slop.ai/mcp
136
136
  Edit a file through MCP:
137
137
 
138
138
  ```sh
139
- npx --yes zero-slop@2.10.0 deslop draft.md --genre professional
139
+ npx --yes zero-slop@2.10.1 deslop draft.md --genre professional
140
140
  ```
141
141
 
142
142
  Use `-` for stdin and `--json` for structured output. `--require-approved` exits nonzero
@@ -254,8 +254,8 @@ stress case took 2.2932 seconds, and learning from an 8,000-word edit took
254
254
  0.1592 seconds. The measurements and machine details are in
255
255
  [`bench/performance-results.json`](bench/performance-results.json).
256
256
 
257
- Across 12 interleaved runs against 2.7.7, we measured 2.57% higher median throughput,
258
- which is effectively unchanged. The separate two-way replay used
257
+ Across 12 interleaved runs against 2.7.7, we measured 0.71% lower median throughput,
258
+ within the 5% regression limit. The separate two-way replay used
259
259
  Zero Slop v2.6.0.
260
260
 
261
261
  The [RAID+ audit](bench/raid-plus-corpus/README.md) checks how the scorer responds
package/SECURITY.md CHANGED
@@ -48,11 +48,25 @@ draft or rewrite.
48
48
 
49
49
  The REST endpoint `https://mcp.zero-slop.ai/v1/deslop` calls this same pipeline.
50
50
  It accepts at most 128 KiB of UTF-8 JSON and one trimmed draft of 20,000 Unicode code
51
- points. REST and MCP share the capacity limiter; adding a transport does not create
52
- an independent allowance. The limiter operates per Cloudflare location and is not
53
- a strict global quota. REST errors omit request content. Responses use `no-store`,
51
+ points. REST and MCP share an edge capacity limiter. That limiter operates per
52
+ Cloudflare location; a separate atomic budget reservation gates every hosted
53
+ editing-model call across REST, MCP, CLI and the browser demo. Adding a transport
54
+ does not create an independent editing allowance. REST errors omit request content. Responses use `no-store`,
54
55
  and there is no stored response replay or idempotency cache.
55
56
 
57
+ The editor fails closed when its shared daily budget or per-client limit is reached,
58
+ or when budget enforcement is unavailable. Capacity is reserved before inference;
59
+ failed or timed-out model calls are not refunded because their usage may be unknown.
60
+ No automatic model retry or paid-model fallback is used. Local scoring is not metered.
61
+ The service derives a keyed daily hash of the trusted connection IP to coordinate
62
+ abuse limits. This separate counter stores the daily hash and counts, never the raw
63
+ IP or draft, and does not join them to analytics. Shared networks can share a limit.
64
+ Active daily identifiers expire after the UTC day. Cloudflare's SQLite recovery
65
+ history may retain earlier database states for up to 30 days; this history contains
66
+ no drafts or raw IP addresses.
67
+ The project budget bounds Zero Slop's reserved model usage, not other applications'
68
+ usage or the account's existing hosting charges.
69
+
56
70
  REST adds completed results, failures, and capacity rejections to the existing
57
71
  aggregate counters. It does not increment MCP initialization or tool-call counts.
58
72
  REST logs contain only event name, outcome, character count, and elapsed time.
@@ -66,6 +80,13 @@ detected phrases, IP addresses, raw user agents, cookies, email addresses, and s
66
80
  user or session identifiers are excluded. Initializations are reported as connections,
67
81
  not unique people. Analytics Engine retains the dataset for three months.
68
82
 
83
+ Hosted events also record the entry channel (MCP, CLI, REST, or the web editor),
84
+ approval category and model-attempt count. The CLI sends a fixed app-family and
85
+ major-version header on its existing hosted requests; this is self-reported
86
+ attribution, not identity. Offline commands send no analytics. The web endpoint
87
+ does not claim to measure the browser's final source checks. Internal signed
88
+ gateway model calls are excluded from the web channel to prevent double counting.
89
+
69
90
  Telemetry writes are non-blocking and wrapped so an analytics failure cannot fail an
70
91
  MCP call. The daily report uses sampling-aware aggregate queries and degrades to a
71
92
  clearly labeled missing section if the dataset cannot be read.
package/SKILL.md CHANGED
@@ -2,7 +2,7 @@
2
2
  name: zero-slop
3
3
  license: MIT
4
4
  metadata:
5
- version: "2.10.0"
5
+ version: "2.10.1"
6
6
  author: manavmishra
7
7
  description: Turn drafts into sharp, natural prose or inspect them without rewriting. Zero Slop runs inside the user's existing AI assistant; Claude, GPT, or another compatible model reads and edits in context while local tools point to exact phrases and protect the source. Use when the user asks to humanize or de-slop writing, inspect AI-sounding patterns, fix text that reads like ChatGPT, polish outward-facing prose, draft social or LinkedIn content, or apply a final quality check to prose the agent generated. The workflow preserves facts, voice, and format and learns privately from repeated, reason-labelled human edits.
8
8
  ---
@@ -183,9 +183,13 @@ export async function deslop(input, options = {}) {
183
183
  let protocolVersion = PROTOCOLS[0];
184
184
  let session;
185
185
  let toolPending = false;
186
+ // Hosted CLI attribution only: no identifier, machine data, or extra request.
187
+ // Other integrations retain their existing clientInfo without posing as CLI.
188
+ const cliMajorVersion = clientVersion.match(/^(\d{1,4})(?:\.|$)/)?.[1] ?? "unknown";
186
189
  const headers = () => ({
187
190
  "content-type": "application/json", accept: "application/json, text/event-stream",
188
191
  "cache-control": "no-store", "mcp-protocol-version": protocolVersion,
192
+ ...(clientName === "zero-slop-cli" ? { "user-agent": `zero-slop-cli/${cliMajorVersion}` } : {}),
189
193
  ...(session ? { "mcp-session-id": session } : {}),
190
194
  });
191
195
  // A dropped HTTP connection is not an MCP cancellation notification. Request it
@@ -210,7 +214,9 @@ export async function deslop(input, options = {}) {
210
214
  void response.body?.cancel().catch(() => {});
211
215
  const retry = response.headers.get("retry-after");
212
216
  const retryAfterSeconds = retry && /^\d{1,5}$/.test(retry) ? Number(retry) : undefined;
213
- throw new DeslopError("http_error", `The MCP service returned HTTP ${response.status}. No retry was sent.`, {
217
+ throw new DeslopError("http_error", response.status === 429
218
+ ? "Zero Slop is busy or at a usage limit. Please wait before trying again. No retry was sent."
219
+ : `The MCP service returned HTTP ${response.status}. No retry was sent.`, {
214
220
  httpStatus: response.status, ...(retryAfterSeconds !== undefined ? { retryAfterSeconds } : {}),
215
221
  });
216
222
  }
@@ -243,6 +249,17 @@ export async function deslop(input, options = {}) {
243
249
  throw new DeslopError("invalid_response", "The MCP service returned an invalid tool result.");
244
250
  }
245
251
  if (reply.isError === true) {
252
+ const gate = reply._meta?.["zero-slop/error"];
253
+ if (gate && (gate.code === "usage_limit" || gate.code === "budget_unavailable") &&
254
+ gate.status === (gate.code === "usage_limit" ? 429 : 503)) {
255
+ const retry = gate.retryAfterSeconds;
256
+ throw new DeslopError(gate.code, gate.code === "usage_limit"
257
+ ? "Hosted editing is temporarily busy or at its free usage limit. Please wait before trying again. Keep your source; no retry was sent."
258
+ : "Hosted capacity could not be checked. Please try again later. Keep your source; no retry was sent.", {
259
+ httpStatus: gate.status,
260
+ ...(Number.isInteger(retry) && retry > 0 && retry <= 86_400 ? { retryAfterSeconds: retry } : {}),
261
+ });
262
+ }
246
263
  throw new DeslopError("tool_error", "Zero Slop could not return a safely checked result. The source was not changed locally.");
247
264
  }
248
265
  return validateResult(reply.structuredContent);
package/docs/cli.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Use `deslop` for hosted editing with MCP parity. Use `score` for offline checks.
4
4
 
5
5
  ```sh
6
- npm install --global zero-slop@2.10.0
6
+ npm install --global zero-slop@2.10.1
7
7
  zero-slop deslop draft.md --genre professional
8
8
  zero-slop deslop - --genre email < draft.txt
9
9
  zero-slop deslop draft.md --json --require-approved
@@ -13,6 +13,16 @@ zero-slop score draft.md -- --json
13
13
  Use Node.js 22 or newer for hosted editing. Offline scoring also requires Python 3.
14
14
  The npm package includes the skill and its local checks; no separate model ships.
15
15
 
16
+ On macOS, [Homebrew](https://github.com/manavmishra/homebrew-zero-slop) installs
17
+ the same npm CLI plus Node and Python:
18
+
19
+ ```sh
20
+ brew install manavmishra/zero-slop/zero-slop
21
+ ```
22
+
23
+ The tap pins a tested npm release; a new npm version may arrive before its
24
+ Homebrew update. Linux Homebrew has not been tested.
25
+
16
26
  `deslop` reads exactly one file or standard input, sends the draft to
17
27
  `https://mcp.zero-slop.ai/mcp`, and writes the returned text to standard output.
18
28
  It never edits a source file in place. Warnings go to standard error. Capture
@@ -49,6 +59,18 @@ Inspect the [six result statuses](rest-api.md#read-the-result). In particular,
49
59
  A timeout or cancellation may not stop hosted processing. The CLI does not replay
50
60
  the request. Review any returned status or error before choosing to send the draft again.
51
61
 
62
+ Hosted editing shares its free allowance with MCP, REST, and `/try/`. A
63
+ `usage_limit` error exits `1`; JSON errors include `httpStatus: 429` and, when
64
+ provided, `retryAfterSeconds`. Wait at least that long before a manual retry.
65
+ `budget_unavailable` (`503`) means capacity could not be checked, so no new model
66
+ request was started. Offline scoring does not consume this allowance.
67
+
68
+ Hosted calls are included in aggregate service metrics: channel, result status,
69
+ model attempts, and latency. A fixed CLI-family/major-version header identifies
70
+ the entry channel on the requests already being sent; no extra tracking request
71
+ is made. It does not identify a person or installation. Offline commands send no
72
+ analytics. See the [security policy](../SECURITY.md) for the complete field list.
73
+
52
74
  ## Offline scoring
53
75
 
54
76
  ```sh
package/docs/rest-api.md CHANGED
@@ -8,10 +8,14 @@ The [OpenAPI 3.1.2 contract](https://mcp.zero-slop.ai/openapi.json) is generated
8
8
  from the schemas used to validate MCP and REST requests and results. Import it
9
9
  into an OpenAPI-compatible client or documentation tool.
10
10
 
11
+ Use the [API sandbox](https://zero-slop.ai/sandbox/) to inspect a simulated result
12
+ before sending a draft. Its live mode requires an explicit request and uses this
13
+ same endpoint.
14
+
11
15
  ## Send a draft
12
16
 
13
17
  ```sh
14
- curl --fail-with-body --max-time 75 https://mcp.zero-slop.ai/v1/deslop \
18
+ curl --fail-with-body --max-time 75 --include https://mcp.zero-slop.ai/v1/deslop \
15
19
  -H 'Content-Type: application/json' \
16
20
  --data '{"text":"It is important to note that Maya owns the pricing review. The team will decide on Friday.","genre":"professional","audience":"The product team"}'
17
21
  ```
@@ -26,9 +30,52 @@ Lengths count Unicode code points; a combined emoji or accented character can co
26
30
  Send UTF-8 JSON in the body, never a URL query. The body limit is 128 KiB, including
27
31
  JSON escaping. Compressed bodies are not accepted. Unknown properties are ignored.
28
32
 
29
- Call from your application server. The service does not enable cross-origin browser
30
- requests. Access is free, without an API key, and shares MCP's capacity limiter.
31
- Capacity is best effort; there is no reserved quota or uptime SLA.
33
+ Call from your application server. Browser access is enabled only for the Zero Slop
34
+ site at `https://zero-slop.ai` and `https://www.zero-slop.ai`; other sites should
35
+ call from their servers. CORS is a browser policy, not authentication.
36
+
37
+ Access is free, without an API key. REST and MCP share capacity and usage limits,
38
+ including a daily hosted allowance and a short client cooldown. The allowance can
39
+ run out; it does not reserve capacity for an application. Availability is best
40
+ effort, with no uptime SLA. Offline skill checks and CLI scoring stay free and do
41
+ not consume this hosted allowance.
42
+
43
+ Current safety limits allow at most five model calls per client network per UTC
44
+ day and two per minute, shared across REST, MCP, CLI editing, and `/try/`.
45
+ Shared networks can reach those limits together. A separate global limit reserves
46
+ up to 8,000 estimated Workers AI neurons per UTC day before inference; it can
47
+ stop calls before the client limit is reached. Reservations are conservative and
48
+ are not refunded after timeouts. New model calls pause during the last minute
49
+ before the UTC reset. These are capacity ceilings, not a guaranteed allocation.
50
+
51
+ The service records aggregate REST call counts, result categories, model attempts,
52
+ quota rejections, and latency. Drafts, rewrites, IP addresses, email addresses and
53
+ stable user identifiers are excluded from this telemetry. Calls are not unique
54
+ users, and model attempts are not billable token counts. See the
55
+ [security policy](../SECURITY.md) for the field list and retention.
56
+
57
+ ## Working language examples
58
+
59
+ The [example directory](../integrations/api-examples/README.md) contains runnable
60
+ clients with 75-second timeouts, structured error handling, and an explicit check
61
+ before the returned text can pass to an automated step.
62
+
63
+ | Language | Source | HTTP client |
64
+ |---|---|---|
65
+ | curl | [Shell example](../integrations/api-examples/curl/deslop.sh) | curl; jq reads the result |
66
+ | JavaScript | [Node.js example](../integrations/api-examples/javascript/deslop.mjs) | Built-in `fetch` |
67
+ | TypeScript | [Typed Node.js example](../integrations/api-examples/typescript/deslop.ts) | Built-in `fetch` |
68
+ | Python | [Python example](../integrations/api-examples/python/deslop.py) | Standard-library `urllib.request` |
69
+ | Java | [Java example](../integrations/api-examples/java/src/main/java/Deslop.java) | JDK `HttpClient`; Jackson for JSON |
70
+ | C# | [.NET example](../integrations/api-examples/csharp/Program.cs) | `HttpClient` and `System.Text.Json` |
71
+ | Go | [Go example](../integrations/api-examples/go/main.go) | Standard-library `net/http` |
72
+ | Rust | [Rust example](../integrations/api-examples/rust/src/main.rs) | reqwest and serde_json |
73
+
74
+ Each example reads one request object from stdin, sends one POST, and preserves
75
+ the complete JSON response. Exit `0` means the approval condition below passed;
76
+ exit `3` means review is required. HTTP and transport failures exit `1`. Check
77
+ the exit code before extracting `text`; an HTTP `200` alone is insufficient.
78
+ These are examples to adapt within an application, not separate SDKs.
32
79
 
33
80
  ## Read the result
34
81
 
@@ -44,9 +91,24 @@ every check.
44
91
  | `unchanged_verification_failed` | Keep the original; proposed edits failed source checks |
45
92
  | `unchanged_service_unavailable` | Keep the original; the model did not yield a usable edit |
46
93
 
47
- The response also contains `before` and `after` writing reports, `scoreChange`,
48
- `factsPreserved`, `passedFinalChecks`, `independentModelChecks`, `modelRequests`,
49
- `rolesCompleted`, `finishingRounds`, `scorerVersion`, `durationMs`, `text`, and `note`.
94
+ The successful response contains these 14 fields:
95
+
96
+ | Field | Meaning |
97
+ |---|---|
98
+ | `text` | Returned draft, which may be unchanged |
99
+ | `status` | One of the six outcomes above |
100
+ | `before`, `after` | Writing reports: score, flagged phrases, readability, rhythm, punctuation, layout, and register checks |
101
+ | `scoreChange` | After score minus before score |
102
+ | `factsPreserved` | Whether tracked source details survived the checks |
103
+ | `passedFinalChecks` | Whether the rewritten text passed the final checks |
104
+ | `independentModelChecks` | Count of independent model checks; currently zero |
105
+ | `modelRequests` | Editing-model requests made, zero or one |
106
+ | `rolesCompleted` | Completed pipeline responsibilities |
107
+ | `finishingRounds` | Additional local finishing rounds |
108
+ | `scorerVersion` | Deployed Zero Slop release |
109
+ | `durationMs` | Server processing time in milliseconds |
110
+ | `note` | Review guidance, including any limitations |
111
+
50
112
  `scoreChange` is after minus before, so a negative value means the measured score fell.
51
113
  The writing score is not a probability that AI wrote the draft.
52
114
 
@@ -73,12 +135,30 @@ Errors use `application/problem+json` with `type`, `title`, `status`, `detail`,
73
135
  | HTTP status | Meaning |
74
136
  |---|---|
75
137
  | `400` | Invalid JSON, UTF-8, declared length, or input fields |
138
+ | `403` | `forbidden_origin` or `forbidden_preflight`; use an allowed first-party browser request or call from your server |
76
139
  | `405` | Wrong method; use `POST` |
77
140
  | `408` | The request body did not arrive within 10 seconds |
78
141
  | `413` | The JSON body exceeds 128 KiB |
79
142
  | `415` | Unsupported media type or compression |
80
- | `429` | Shared capacity reached; respect `Retry-After` |
81
- | `503` | No safely scored result was available |
143
+ | `429` | `capacity_limit` or `usage_limit`; respect `Retry-After` |
144
+ | `503` | `budget_unavailable` if the usage gate could not complete, or `service_unavailable` if no safely scored result was available |
145
+
146
+ For example, a usage-limit response has this shape; values here are illustrative:
147
+
148
+ ```json
149
+ {
150
+ "type": "about:blank",
151
+ "title": "Too Many Requests",
152
+ "status": 429,
153
+ "detail": "The free hosted allowance is currently exhausted. Try again after the indicated delay.",
154
+ "code": "usage_limit",
155
+ "requestId": "11111111-1111-4111-8111-111111111111"
156
+ }
157
+ ```
158
+
159
+ The `Retry-After` response header gives the minimum delay in seconds. Read it
160
+ from the header rather than parsing `detail`. Keep the problem code and request
161
+ ID for troubleshooting without logging the draft or result.
82
162
 
83
163
  Allow 75 seconds on the client. The service makes at most one editing-model request;
84
164
  scoring and source checks add their own bounded work. A client timeout or cancellation
@@ -92,7 +172,8 @@ calls, may repeat work, and are not guaranteed to return identical text.
92
172
 
93
173
  The draft is sent to Zero Slop's hosted service and processed in memory. The upstream
94
174
  editing service must confirm `stored: false`; drafts and rewrites are not cached or logged by this
95
- service. Aggregate operational counts remain. See [SECURITY.md](../SECURITY.md) and
175
+ service. Aggregate operational counts and short-lived daily hashed client counters
176
+ support usage limits and abuse controls. See [SECURITY.md](../SECURITY.md) and
96
177
  the [hosted-service terms](https://zero-slop.ai/terms/).
97
178
 
98
179
  `/v1` identifies the HTTP contract. `scorerVersion` identifies the deployed Zero Slop
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zero-slop",
3
- "version": "2.10.0",
3
+ "version": "2.10.1",
4
4
  "description": "Find and fix canned AI writing while preserving the facts, voice, and format.",
5
5
  "mcpServers": {
6
6
  "zero-slop": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zero-slop",
3
- "version": "2.10.0",
3
+ "version": "2.10.1",
4
4
  "description": "An editing skill, offline writing checks, and a CLI for the hosted Zero Slop MCP pipeline. Find stock AI phrasing, rewrite, and check source details.",
5
5
  "bin": {
6
6
  "zero-slop": "bin/zero-slop.mjs"
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "zero-slop",
4
- "version": "2.10.0",
4
+ "version": "2.10.1",
5
5
  "description": "Find and fix canned AI writing while preserving the facts, voice, and format.",
6
6
  "author": {
7
7
  "name": "Manav Mishra",