@zackbart/connecta 0.27.0 → 0.28.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.
Files changed (42) hide show
  1. package/AGENTS.md +6 -6
  2. package/CHANGELOG.md +56 -0
  3. package/README.md +15 -19
  4. package/bin/connecta.mjs +1 -5
  5. package/dist/activity.d.ts +5 -4
  6. package/dist/activity.js +1 -10
  7. package/dist/artifacts/json.js +10 -0
  8. package/dist/artifacts/operations.js +1 -1
  9. package/dist/artifacts/validate.js +1 -2
  10. package/dist/catalog-service.js +1 -1
  11. package/dist/execute.js +92 -368
  12. package/dist/executors/quickjs.js +1 -1
  13. package/dist/exempt-writes.js +162 -0
  14. package/dist/index.d.ts +12 -31
  15. package/dist/index.js +14 -47
  16. package/dist/invocation.js +6 -19
  17. package/dist/meta-tools.js +1 -2
  18. package/dist/operator-ui/generated.js +1 -1
  19. package/dist/operator-ui/model.d.ts +3 -3
  20. package/dist/operator-ui/view.js +6 -4
  21. package/dist/routes/mcp.js +5 -21
  22. package/dist/routes/shared.d.ts +1 -6
  23. package/dist/server.js +0 -4
  24. package/dist/skills.js +8 -23
  25. package/dist/tool-safety.d.ts +1 -1
  26. package/dist/tool-safety.js +1 -1
  27. package/dist/types.d.ts +2 -2
  28. package/dist/version.d.ts +1 -1
  29. package/dist/version.js +1 -1
  30. package/documentation/architecture.md +10 -14
  31. package/documentation/code-mode.md +91 -194
  32. package/documentation/meta-tools.md +38 -41
  33. package/ethos.md +6 -7
  34. package/examples/worker/README.md +10 -14
  35. package/examples/worker/src/d1-activity-row.ts +4 -1
  36. package/examples/worker/src/index.ts +1 -9
  37. package/package.json +2 -2
  38. package/templates/node/AGENTS.md +4 -3
  39. package/templates/node/README.md +3 -4
  40. package/templates/node/package.json +1 -1
  41. package/dist/resumable.js +0 -1323
  42. package/dist/run-journal.js +0 -347
package/AGENTS.md CHANGED
@@ -4,13 +4,13 @@ This is the canonical instruction file for coding agents. `CLAUDE.md` is a
4
4
  symlink to this file so every agent works from the same conventions.
5
5
 
6
6
  A single MCP endpoint aggregating downstream connectors (remote MCP servers and
7
- plain HTTP APIs) behind eight meta-tools, `execute_code` among them. Every
7
+ plain HTTP APIs) behind seven meta-tools, `execute_code` among them. Every
8
8
  deployment configures an executor, and agents reach connectors by writing
9
9
  JavaScript against it. One fetch-native core on Effect behind a Promise API,
10
- running on both Node and Cloudflare Workers. A program's write pauses until
11
- `resume_execution` repeats it. One `planned` decision changes that picture — an
12
- artifacts module — and until it ships, the code and the guides describe what
13
- exists.
10
+ running on both Node and Cloudflare Workers. A program runs read-only and
11
+ config-exempt tools; every other write is its own `call_destructive_tool` call,
12
+ so the host's permission prompt is the only approval. The code and the guides
13
+ describe what exists.
14
14
 
15
15
  - **[`ethos.md`](./ethos.md) is the constitution.** It states what connecta is
16
16
  and isn't, and its decisions table carries a verdict for every shape already
@@ -74,7 +74,7 @@ pins the generated deployment to the CLI package's exact version, restores the
74
74
  template `.gitignore` and the `CLAUDE.md` symlink, and refuses to merge into an
75
75
  existing path.
76
76
  `connecta doctor` verifies a running deployment's health, executor, and exact
77
- eight-tool surface. The template carries its own `Dockerfile` and
77
+ seven-tool surface. The template carries its own `Dockerfile` and
78
78
  `docker-compose.yml`, so the generated project is the container: setup changes
79
79
  must keep the root README, the template (source, container files, and README),
80
80
  and the `scripts/check-package.mjs` smoke — which builds and runs that
package/CHANGELOG.md CHANGED
@@ -2,6 +2,62 @@
2
2
 
3
3
  All notable changes to this package are documented here.
4
4
 
5
+ ## 0.28.0 — 2026-10-04
6
+
7
+ This minor release removes program pauses ([#672](https://github.com/zackbart/connecta/issues/672)).
8
+ `execute_code` programs run explicitly read-only tools and the writes
9
+ `execute.approval` exempts, and every other write is its own
10
+ `call_destructive_tool` call, so the host's permission prompt on that call is
11
+ the only approval there is. Hosts approve one tool call at a time, and under
12
+ always-allow or bypass modes the agent called `resume_execution` itself: each
13
+ pause cost a round trip and a full replay and protected nothing. What breaks:
14
+ `resume_execution` is gone, so the surface is seven tools again, and a host
15
+ allow-list or cached tool list naming it should drop it.
16
+ `execute.resumableWrites` and `execute.pausedRunTtlSeconds` now refuse to
17
+ construct with an error naming
18
+ the removal — delete them, including the Worker example's
19
+ `execute: { resumableWrites: false }` line. A program's non-exempt write now
20
+ fails `destructive_tool_requires_approval` with a `nextAction` for
21
+ `call_destructive_tool`, exactly as it did on deployments without resumable
22
+ writes, and a run never returns `{ paused: … }`. `Date.now()` and
23
+ `Math.random()` inside a program are the runtime's own again rather than pinned
24
+ per run. A deployment that sets neither option and whose programs only read
25
+ can ignore all of this: approval exemptions, `execute.maxWrites`, storage
26
+ contracts, and the D1 activity schema are unchanged, and any paused runs left
27
+ in storage expire on their own TTL. The Node template now pins 0.28.0.
28
+
29
+ ### Removed
30
+
31
+ - `resume_execution`, the run journal, deterministic replay, compare-and-set
32
+ run claims, and the pinned clock and randomness (code-mode `P6`) that
33
+ existed only so a replay could match. `W1`–`W8`, `W11`, `P6`, and `X12` are
34
+ retired clause ids.
35
+ - The pause-only error codes: `execution_paused`, `pending_write_too_large`,
36
+ `journal_too_large`, `execution_interrupted`, `execution_diverged`,
37
+ `execution_claim_lost`, `approval_mismatch`, `execution_token_stale`,
38
+ `execution_in_progress`, `execution_expired`, `execution_not_found`, and
39
+ `resumable_writes_unavailable`. `write_outcome_unknown` stays, for an exempt
40
+ write that was sent and never answered.
41
+ - `resumableWrites` from `/health`, and the "resumable writes off" note from
42
+ `connecta doctor`, which now verifies the exact seven-tool surface.
43
+ - The pause and resume agent eval tasks.
44
+
45
+ ### Changed
46
+
47
+ - The MCP instructions, the `execute_code` description, and the `usage` skill
48
+ describe the read-only program route: reads and config-exempt writes in a
49
+ program, every other write through `call_destructive_tool`. The description
50
+ states the exempt-write budget.
51
+ - `execute.maxWrites` now bounds only config-exempt writes, the one kind a
52
+ program can still send. Its default stays 10.
53
+ - Activity no longer emits `paused` or `approved` outcomes, the
54
+ `resume_execution` source, or an `approval` scope. The types keep them as
55
+ read-only history, the operator UI still renders older rows (a historical
56
+ pause now reads "Paused for approval"), and the D1 example still stores and
57
+ reads the `approval` column.
58
+ - QuickJS still runs its child with `TZ=UTC`, now for parity with a Dynamic
59
+ Worker's local time rather than for replay.
60
+
5
61
  ## 0.27.0 — 2026-10-01
6
62
 
7
63
  This minor release moves the runtime to stable Effect v4 and fixes execution,
package/README.md CHANGED
@@ -10,19 +10,18 @@ writing code instead of loading a thousand tool definitions.
10
10
  You ask your agent a question that touches a service — Linear, Stripe, an
11
11
  internal API, anything you have connected. Here is what happens:
12
12
 
13
- 1. The agent talks to one endpoint, yours, and sees eight tools. Always eight,
13
+ 1. The agent talks to one endpoint, yours, and sees seven tools. Always seven,
14
14
  no matter how many services sit behind it.
15
15
  2. It writes a short JavaScript program. Connecta runs it in a sandbox next to
16
16
  your integrations. The program can search for tools, call them, chain the
17
17
  calls, and shape the result.
18
18
  3. Only the answer comes back into the agent's context — not raw pages of
19
19
  API output.
20
- 4. If the program wants to change something — create, update, delete — it
21
- stops right there, before anything is sent, and hands the agent the exact
22
- write. The agent repeats it through `resume_execution`, which your MCP
23
- client can put in front of you first. Approve it and the program picks up
24
- where it stopped, without re-reading what it already read. A single write
25
- can also go straight through `call_destructive_tool`.
20
+ 4. If the agent wants to change something — create, update, delete — it
21
+ cannot do that from a program, unless your config exempts that tool. The
22
+ program refuses the write before anything is sent and hands back the exact
23
+ call; the agent makes it through `call_destructive_tool`, one visible call
24
+ your MCP client can put in front of you first.
26
25
 
27
26
  Credentials never leave the server. The program never sees them, and neither
28
27
  does the agent.
@@ -31,9 +30,9 @@ does the agent.
31
30
  flowchart TB
32
31
  Client["Your MCP client<br/>Claude, Cursor, …"]
33
32
 
34
- subgraph Connecta["Connecta — one endpoint, eight tools, your credentials"]
35
- Sandbox["execute_code<br/>the agent's program runs here<br/>reads run, writes pause"]
36
- Explicit["resume_execution · call_destructive_tool<br/>the exact write, visible<br/>your client can ask you first"]
33
+ subgraph Connecta["Connecta — one endpoint, seven tools, your credentials"]
34
+ Sandbox["execute_code<br/>the agent's program runs here<br/>read-only tools, plus writes<br/>your config exempts"]
35
+ Explicit["call_destructive_tool<br/>one visible call per write<br/>your client can ask you first"]
37
36
  end
38
37
 
39
38
  Integrations["The integrations you chose<br/>Linear · Stripe · Notion · Vercel · your HTTP API · any MCP server"]
@@ -41,7 +40,6 @@ flowchart TB
41
40
  Client -->|"one connection"| Sandbox
42
41
  Client --> Explicit
43
42
  Sandbox -->|"reads"| Integrations
44
- Sandbox -.->|"paused write"| Explicit
45
43
  Explicit -->|"writes"| Integrations
46
44
  ```
47
45
 
@@ -82,13 +80,11 @@ Fifty issues in, one small object out. Your context window notices.
82
80
  notice also points the agent to reduce or search the result inside a
83
81
  program. Discovery can show compact schemas, exact JSON Schema, or a
84
82
  TypeScript signature to read while writing JavaScript.
85
- - **Keep writes deliberate.** Only tools marked read-only run in a program
86
- unasked. Any other call pauses the program with the exact write, and it
87
- runs only once `resume_execution` repeats it — a visible call your client
88
- can gate. A paused run is a journal in storage, not a held program, so it
89
- survives a restart; it expires, and a write whose outcome is unknown is
90
- never sent twice. Config — and only config — can exempt a cheap,
91
- reversible write from asking, per tool or per connector.
83
+ - **Keep writes deliberate.** Only tools marked read-only run in a program.
84
+ Every other write is a separate, visible `call_destructive_tool` call your
85
+ client can gate, so the host's permission prompt is the one approval there
86
+ is. Config — and only config — can exempt a cheap, reversible write from
87
+ asking, per tool or per connector, and then a program may make it.
92
88
  - **Run it on Node or Cloudflare Workers.** The core is shared; each deployment
93
89
  supplies its platform's executor and storage. The Node template also runs
94
90
  unchanged in Docker.
@@ -122,7 +118,7 @@ and why.
122
118
 
123
119
  Setup is written for an agent. Point yours at [`AGENTS.md`](./AGENTS.md) and
124
120
  ask it to set up a Connecta deployment; the
125
- [documentation](./documentation/) covers the architecture, the eight tools,
121
+ [documentation](./documentation/) covers the architecture, the seven tools,
126
122
  code mode, and inbound auth if you want to go deeper. When upgrading an
127
123
  existing deployment, each [changelog](./CHANGELOG.md) release opens with what
128
124
  breaks and what a deployment can ignore.
package/bin/connecta.mjs CHANGED
@@ -257,7 +257,6 @@ async function doctor() {
257
257
  "call_tool",
258
258
  "execute_code",
259
259
  "get_result",
260
- "resume_execution",
261
260
  "search_tools",
262
261
  "skills",
263
262
  ];
@@ -310,10 +309,7 @@ async function doctor() {
310
309
  console.log(
311
310
  `Connecta doctor passed: ${health.connectors} connector(s), ` +
312
311
  `${executorName ? `${executorName} executed` : "code executed"}, ` +
313
- "prescribed eight-tool surface" +
314
- // resume_execution is listed either way; whether programs can write is
315
- // what an operator cannot tell from the tool list, so say when not.
316
- (health.resumableWrites === false ? ", resumable writes off" : "") +
312
+ "prescribed seven-tool surface" +
317
313
  (drifted.length > 0
318
314
  ? `, catalog drift on ${drifted.length} connector(s).`
319
315
  : "."),
@@ -2,7 +2,7 @@ import type { ActivityModule } from "./module-contracts.js";
2
2
  import type { CatalogDriftCounts, Logger } from "./types.js";
3
3
  export type ActivityCallSource = "call_tool" | "call_destructive_tool" | "batch_call" | "execute_code" | "resume_execution";
4
4
  export type ActivityOutcome = "success" | "error" | "timeout" | "cancelled" | "paused" | "approved";
5
- /** How much one `resume_execution` approved: this call, or this tool for the run. */
5
+ /** Read-only history: how much one `resume_execution` approved (#672). */
6
6
  export type ActivityApproval = "call" | "tool";
7
7
  export type AgentFriction = "tool_not_found" | "schema_retry" | "destructive_reroute" | "auth_required" | "result_too_large";
8
8
  export { agentFrictionForCode } from "./activity-friction.js";
@@ -50,8 +50,9 @@ export interface ToolCallActivityEvent {
50
50
  */
51
51
  friction?: AgentFriction;
52
52
  /**
53
- * Set only on an `approved` event: the scope the approval covered. An enum,
54
- * like everything else here — the approved arguments are never recorded.
53
+ * Read-only history: set only on an `approved` event written before issue
54
+ * #672, the scope the approval covered. An enum, like everything else
55
+ * here — the approved arguments were never recorded.
55
56
  */
56
57
  approval?: ActivityApproval;
57
58
  serverName: string;
@@ -149,7 +150,7 @@ export interface ActivityRequestContext {
149
150
  defer?: (promise: Promise<unknown>) => void;
150
151
  logger: Logger;
151
152
  }
152
- export type ActivityEventInput = Pick<ToolCallActivityEvent, "connectorId" | "toolName" | "address" | "source" | "outcome" | "durationMs" | "attempts" | "errorCode" | "friction" | "approval">;
153
+ export type ActivityEventInput = Pick<ToolCallActivityEvent, "connectorId" | "toolName" | "address" | "source" | "outcome" | "durationMs" | "attempts" | "errorCode" | "friction">;
153
154
  /**
154
155
  * Best-effort by design: activity storage can never change a tool result.
155
156
  * Workers attach async sinks to waitUntil; synchronous sinks such as Analytics
package/dist/activity.js CHANGED
@@ -49,18 +49,9 @@ export function recordToolActivity(context, input) {
49
49
  source: input.source,
50
50
  outcome: input.outcome,
51
51
  durationMs: Math.max(0, Math.trunc(input.durationMs)),
52
- // A pause and an approval reach nothing downstream, so they are the two
53
- // events that honestly carry zero attempts. Every other outcome keeps its
54
- // floor of one, as it always has.
55
- attempts: input.outcome === "paused" || input.outcome === "approved"
56
- ? 0
57
- : Math.max(1, Math.trunc(input.attempts)),
52
+ attempts: Math.max(1, Math.trunc(input.attempts)),
58
53
  ...(input.errorCode ? { errorCode: input.errorCode } : {}),
59
54
  ...(friction ? { friction } : {}),
60
- ...(input.outcome === "approved" &&
61
- (input.approval === "call" || input.approval === "tool")
62
- ? { approval: input.approval }
63
- : {}),
64
55
  serverName: context.serverInfo.name,
65
56
  serverVersion: context.serverInfo.version,
66
57
  ...(context.deploymentId
@@ -66,3 +66,13 @@ export function jsonProblem(value, maxDepth) {
66
66
  }
67
67
  return undefined;
68
68
  }
69
+ const encoder = new TextEncoder();
70
+ /** A string's size in UTF-8 bytes: what every artifact limit counts. */
71
+ export function utf8Bytes(text) {
72
+ return encoder.encode(text).byteLength;
73
+ }
74
+ /** SHA-256 as lowercase hex: the content address of a stored body. */
75
+ export async function sha256Hex(text) {
76
+ const digest = await crypto.subtle.digest("SHA-256", encoder.encode(text));
77
+ return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
78
+ }
@@ -14,8 +14,8 @@
14
14
  // Nothing here throws for a caller's mistake. Operations answer
15
15
  // `{ ok: false, code, message }`, and the connector turns that into its typed
16
16
  // error; a throw means storage failed.
17
- import { sha256Hex, utf8Bytes } from "../run-journal.js";
18
17
  import { buildFrameDocument, FRAME_OVERHEAD_BYTES } from "./document.js";
18
+ import { sha256Hex, utf8Bytes } from "./json.js";
19
19
  import { markdownPage, MarkdownNestingError } from "./markdown.js";
20
20
  import { ARTIFACT_ID, checkDocument, checkDocumentTotals, checkView, finish, validDocumentName, } from "./validate.js";
21
21
  /** Physical head moves a write absorbs before giving up as busy. */
@@ -7,9 +7,8 @@
7
7
  // finding names a line where there is one, quotes the source only through the
8
8
  // shared echo bound, and says what to do instead.
9
9
  import { boundedEchoText } from "../errors.js";
10
- import { utf8Bytes } from "../run-journal.js";
11
10
  import { lineLocator, scanHtml } from "./html-scan.js";
12
- import { jsonProblem, scriptSafeJson } from "./json.js";
11
+ import { jsonProblem, scriptSafeJson, utf8Bytes } from "./json.js";
13
12
  import { FRAME_OVERHEAD_BYTES } from "./document.js";
14
13
  import { markdownPage, MarkdownNestingError } from "./markdown.js";
15
14
  import { DEFAULT_ARTIFACT_ALLOWLIST, DEFAULT_ARTIFACT_LIMITS, } from "./types.js";
@@ -538,7 +538,7 @@ export class CatalogService {
538
538
  ...(match.tool.annotations
539
539
  ? { annotations: match.tool.annotations }
540
540
  : {}),
541
- // Config lets a program call this write without pausing (#566).
541
+ // Config lets a program call this write without asking (#566).
542
542
  // A marker, not a class: the tool stays approval-required.
543
543
  ...(isApprovalExempt(this.approval, match.connector, match.tool.name, match.tool)
544
544
  ? { approval: "exempt" }