basecradle 0.6.0 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b1754f11512dff348b826735816a0585c0e7f86c8390e6ccf232dcb97cb6a97
4
- data.tar.gz: 4f0dfd1f553019eec050022105c6188e8878d8cab50a8d70d005a24a6b8ee4e8
3
+ metadata.gz: bdd852de1690fbb03f684277c2608371ed8828eb7850d4df6a00cf4de2a8cd09
4
+ data.tar.gz: 80e7e89292022879aafccaa7b9676329c27c9c7692e83ed5e2a78540dbb4b082
5
5
  SHA512:
6
- metadata.gz: ca33b897c56bc93edc8e418db6fd3dcba0d769818722291e335521ff4bc422ec56c191312262e09879607bd540e178a8dc6a69243a29bad8f5e846a9ecc14f09
7
- data.tar.gz: 1368bd93a8776708743cf4324ba82859b91e21b0d8cdbf65b884334bd0eba478ba015e842f54379efd4d43680b2b76d9ae5a5cc65215e88d1720eee43116f647
6
+ metadata.gz: 4ac044722ef655b8de4b7ff25d55fb965d48a7ba49a30a7edcad8ee619e8f279cec1ec5103b6dbed2dfd6b80d1d1e368dc05921a9a13774f95c326597c9dac24
7
+ data.tar.gz: 65f53e1238853a1be5f567be771ba9c808592ff7d4d428743659c6d1abe0b8caf4a7a47af29b0bc4176d42388ee417cf49f92be35d97b5fd649204926c2052bb
data/CHANGELOG.md CHANGED
@@ -4,6 +4,42 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.6.1] - 2026-09-23
8
+
9
+ ### Changed
10
+
11
+ - **Reads both wire shapes across the platform's coming breaking release** — the SDK now
12
+ accepts today's shapes *and* the ones
13
+ [core #585](https://github.com/basecradle/basecradle/issues/585) introduces, so a client
14
+ on this version keeps working across the platform deploy, whichever side of it it is on.
15
+ Every call site reads as before but one, called out below.
16
+ - A webhook event's `webhook_endpoint` becomes the endpoint's full subject form instead
17
+ of a bare reference. It now wraps as a `BaseCradle::WebhookEndpoint` when the payload
18
+ carries `content` — so `event.webhook_endpoint.content.uuid` reads, and the endpoint's
19
+ verbs (`disable` / `enable` / `rotate`) are reachable from an event — and still wraps
20
+ as a `BaseCradle::Reference` when a reference arrives. **The one caller-visible
21
+ change:** `event.webhook_endpoint.uuid` reads the reference shape only, and stops
22
+ resolving once the core deploys. Take the endpoint uuid with
23
+ `BaseCradle.uuid_of(event.webhook_endpoint)`, which yields it from either shape — it is
24
+ what `bc.webhook_events.filter(endpoint:)` uses, so filtering is unaffected.
25
+ - Acting on an endpoint read off an event no longer rewrites that event:
26
+ `event.webhook_endpoint.rotate` updates the endpoint object and leaves the event's
27
+ record of the delivery — including the ingest URL that was live at receipt — intact.
28
+ - `timeline.lock` reads the confirmed `locked` from the new `{"timeline" => ...}`
29
+ envelope or from today's bare `{uuid, locked}` body.
30
+ - `timeline.add_participant` takes the added user from the new `{"user" => ...}`
31
+ envelope or from today's bare nested-actor body, and rosters whichever it got.
32
+ - A `webhook_event` item in `timeline.items` no longer carries `user` — a webhook event
33
+ has no author. Reading `item.user` there raises `BaseCradle::MissingFieldError` (the
34
+ SDK never invents a value the platform withheld), so branch on `item.type` when you
35
+ walk a mixed page. Documented on `BaseCradle::TimelineItem`.
36
+ - `PATCH /users/password` moving from `200` + a body to `204` needs no change: the SDK
37
+ does not wrap that endpoint, and `Client#request` already treats any 2xx as success.
38
+ - The platform's additive fields in the same release (`updated_at` everywhere, an
39
+ endpoint's `user`, `verified_at_receipt`, the full `POST /session` session object) are
40
+ readable today via `[]` and get typed accessors in a follow-up once the core deploys.
41
+ ([#164](https://github.com/basecradle/basecradle-ruby/issues/164))
42
+
7
43
  ## [0.6.0] - 2026-07-17
8
44
 
9
45
  ### Added
@@ -60,11 +96,14 @@ All notable changes to this project are documented here. The format is based on
60
96
  platform treats it opaquely). When given, it is sent as the `Idempotency-Key` request
61
97
  header. The platform stores **at most one record per key** (scoped per timeline + author;
62
98
  per timeline for authorless webhook endpoints), so a replayed keyed create returns the
63
- **original record** — no duplicate record, firehose event, or task activation. A key
64
- identifies one logical create: the same key with a different body still returns the
65
- original record. Keys never expire and never appear in a response. Mirrors the platform's
66
- new capability ([core #328](https://github.com/basecradle/basecradle/issues/328),
67
- shipped in lockstep with the Python SDK).
99
+ **original record** — no duplicate record, no second **Event Delivery** event, no task
100
+ activation. (Event Delivery is the platform's *outbound* push through your integration;
101
+ the webhook endpoints named above are the *inbound* feature the SDK models opposite
102
+ directions, different features.) A key identifies one logical create: the same key with a
103
+ different body still returns the original record. Keys never expire and never appear in a
104
+ response. Mirrors the platform's new capability
105
+ ([core #328](https://github.com/basecradle/basecradle/issues/328), shipped in lockstep
106
+ with the Python SDK).
68
107
  ([#108](https://github.com/basecradle/basecradle-ruby/issues/108))
69
108
  - **Opt-in automatic retries** — `BaseCradle::Client.new(max_retries: 2)` (and
70
109
  `Client.login(..., max_retries:)`) retries requests that are lost on the wire (a timeout
@@ -89,10 +128,11 @@ All notable changes to this project are documented here. The format is based on
89
128
  (`404`). Mirrors the platform's new capability
90
129
  ([core PR #315](https://github.com/basecradle/basecradle/pull/315)), shipped in lockstep
91
130
  with the Python SDK. ([#73](https://github.com/basecradle/basecradle-ruby/issues/73))
92
- - The platform's new terminal **`timeline.deleted`** firehose event — fired to everyone
93
- who was a viewer at deletion, with a `resource` pointer that then `404`s — is documented
94
- alongside `timeline.delete`. The SDK exposes no firehose event-name enum to extend, so
95
- there is no new type or constant; the semantics are captured in the docs.
131
+ - The platform's new terminal **`timeline.deleted`** event — the outbound **Event
132
+ Delivery** fired to everyone who was a viewer at deletion, with a `resource` pointer that
133
+ then `404`s — is documented alongside `timeline.delete`. The SDK exposes no Event Delivery
134
+ event-name enum to extend, so there is no new type or constant; the semantics are captured
135
+ in the docs.
96
136
 
97
137
  ## [0.2.0] - 2026-06-10
98
138
 
@@ -139,7 +179,7 @@ the Python SDK's behavior in idiomatic Ruby. Zero runtime dependencies.
139
179
  with the lazy composable `.filter`. Asset upload is multipart (a path or an IO); tasks
140
180
  accept a `Time`/`DateTime` or an ISO 8601 string.
141
181
  - **Webhooks** — endpoints (`create`, `enable`, `disable`, `rotate`) handing out an
142
- ingest URL, and read-only delivery events.
182
+ ingest URL, and read-only inbound Webhook Events.
143
183
  - **Sessions** — self-credential management: list, `revoke`, and `revoke_all` (sharp by
144
184
  design, never blocked).
145
185
  - **Users & trust** — the directory, access-tiered profiles, and the `grant_trust` /
@@ -151,6 +191,8 @@ the Python SDK's behavior in idiomatic Ruby. Zero runtime dependencies.
151
191
  - **Quality bars** — a README-as-tested-doc harness (every example runs against a mocked
152
192
  API) and a spec drift-guard (CI fails if the live API grows beyond the SDK).
153
193
 
194
+ [0.6.1]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.6.1
195
+ [0.6.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.6.0
154
196
  [0.5.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.5.0
155
197
  [0.4.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.4.0
156
198
  [0.3.0]: https://github.com/basecradle/basecradle-ruby/releases/tag/v0.3.0
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # BaseCradle Ruby SDK
2
2
 
3
- The official Ruby SDK for [BaseCradle](https://basecradle.com) — a communications platform and AI research lab where **humans and AI are equal peers**: same accounts, same permissions, same API.
3
+ The official Ruby SDK for [BaseCradle](https://basecradle.com) — an AI Research Lab and Modular Agentic Framework where **humans and AI are equal peers** same accounts, same permissions, same API.
4
4
 
5
5
  > **Status: 0.x, built in the open.** The [issues](https://github.com/basecradle/basecradle-ruby/issues) are the roadmap; the [changelog](CHANGELOG.md) is the history. The [BaseCradle Python SDK](https://github.com/basecradle/basecradle-python) is the behavioral reference; the API it wraps is live and fully documented: [prose docs](https://basecradle.com/docs/api) · [OpenAPI spec](https://basecradle.com/docs/api.yaml) · [interactive reference](https://basecradle.com/docs/api/reference)
6
6
 
@@ -65,6 +65,8 @@ puts me.documentation.openapi # the API's machine contract, if you want i
65
65
 
66
66
  Every attribute mirrors the API's JSON exactly — what you read in the [API docs](https://basecradle.com/docs/api) is what you type here.
67
67
 
68
+ Your own identity also carries three read-only fields — `integration_url`, `integration_enabled`, and `integration_failure_count`. They report the status of your **integration**: the outbound connection the platform sends **Event Delivery** through. Like the rest of the self/admin cluster they are present on `bc.me.identity` (or an admin's view) and withheld elsewhere, where reading one raises `BaseCradle::MissingFieldError`. Configuring an integration is not an SDK surface — the SDK reports its status; it never sets the URL or flips the switch.
69
+
68
70
  ## Timelines
69
71
 
70
72
  Timelines are the platform's container. Iteration paginates automatically — cursors never appear in your code.
@@ -84,7 +86,7 @@ timeline.lock # the emergency stop: one-way, any viewer can pull it
84
86
  timeline.delete # owner-only, permanent: removes the timeline and all its contents
85
87
  ```
86
88
 
87
- `delete` is owner-only (an admin may delete any timeline; a participant gets `BaseCradle::NotTimelineOwnerError`, a `ForbiddenError`), permanent, and cascades to every message, asset, task, and webhook on the timeline. A locked timeline is still deletable. Viewers receive a terminal `timeline.deleted` firehose event whose resource pointer then 404s.
89
+ `delete` is owner-only (an admin may delete any timeline; a participant gets `BaseCradle::NotTimelineOwnerError`, a `ForbiddenError`), permanent, and cascades to every message, asset, task, and webhook on the timeline. A locked timeline is still deletable. Viewers receive a terminal `timeline.deleted` Event Delivery event whose resource pointer then 404s.
88
90
 
89
91
  ## Messages, assets, tasks
90
92
 
@@ -122,7 +124,7 @@ end
122
124
 
123
125
  ## Webhooks
124
126
 
125
- External services deliver into a timeline by POSTing to an endpoint's secret ingest URL. Each delivery becomes a readable event.
127
+ External services deliver into a timeline by POSTing to an endpoint's secret ingest URL. Each delivery becomes a readable event. This is the **inbound** direction — data arriving at BaseCradle. Its outbound counterpart is Event Delivery, the platform's push through your integration, which the SDK does not model.
126
128
 
127
129
  ```ruby
128
130
  require "basecradle"
@@ -40,7 +40,9 @@ module BaseCradle
40
40
  end
41
41
 
42
42
  # Declare a wire field. +wrap:+ names a model class to wrap the value in (a Hash
43
- # becomes that model; an Array of Hashes becomes an Array of that model).
43
+ # becomes that model; an Array of Hashes becomes an Array of that model), or a
44
+ # callable that picks the class from the payload — how a field whose wire shape is
45
+ # mid-migration reads both forms.
44
46
  def self.attribute(name, wrap: nil)
45
47
  key = name.to_s
46
48
  define_method(name) do
@@ -85,17 +87,23 @@ module BaseCradle
85
87
  "automatically."
86
88
  end
87
89
 
88
- def wrap_value(value, klass)
90
+ def wrap_value(value, wrap)
89
91
  case value
90
92
  when Hash
91
- klass.new(value, client: @client)
93
+ wrap_one(value, wrap)
92
94
  when Array
93
- value.map { |item| item.is_a?(Hash) ? klass.new(item, client: @client) : item }
95
+ value.map { |item| item.is_a?(Hash) ? wrap_one(item, wrap) : item }
94
96
  else
95
97
  value
96
98
  end
97
99
  end
98
100
 
101
+ # +wrap+ is a model class, or a callable returning the class for this payload.
102
+ def wrap_one(data, wrap)
103
+ klass = wrap.respond_to?(:call) ? wrap.call(data) : wrap
104
+ klass.new(data, client: @client)
105
+ end
106
+
99
107
  def raise_missing(key)
100
108
  raise MissingFieldError,
101
109
  "The API did not return #{key.inspect} for this #{self.class}. It may be " \
@@ -8,6 +8,10 @@ require_relative "webhooks"
8
8
  module BaseCradle
9
9
  # One item on a timeline — a message, asset, webhook event, or task. +type+ says which;
10
10
  # +content+ is the item itself, wire-exact; +user+ is the author.
11
+ #
12
+ # A +webhook_event+ item has no author — it was posted by an external sender, not a peer
13
+ # — so the platform omits +user+ there (core #585) and reading it raises
14
+ # +MissingFieldError+. Branch on +type+ when you walk a mixed page of items.
11
15
  class TimelineItem < ApiObject
12
16
  attribute :type
13
17
  attribute :created_at
@@ -35,7 +39,9 @@ module BaseCradle
35
39
  # it is idempotent and one-way (unlocking is an out-of-band admin action).
36
40
  def lock
37
41
  response = require_client.request("POST", "/timelines/#{uuid}/lock")
38
- to_h["locked"] = response["locked"]
42
+ # The response is moving from a bare {uuid, locked} to the timeline envelope
43
+ # (core #585); read the confirmed state off whichever shape arrived.
44
+ to_h["locked"] = (response["timeline"] || response)["locked"]
39
45
  self
40
46
  end
41
47
 
@@ -47,7 +53,7 @@ module BaseCradle
47
53
  # A locked timeline is still deletable: locking freezes content, not governance.
48
54
  # Returns nil — the timeline is gone, so there is nothing left to return. A subsequent
49
55
  # fetch of this uuid raises NotFoundError, and viewers receive a terminal
50
- # +timeline.deleted+ firehose event whose resource pointer now 404s.
56
+ # +timeline.deleted+ Event Delivery event whose resource pointer now 404s.
51
57
  def delete
52
58
  require_client.request("DELETE", "/timelines/#{uuid}")
53
59
  nil
@@ -60,9 +66,12 @@ module BaseCradle
60
66
  response = conn.request(
61
67
  "POST", "/timelines/#{uuid}/participations", json: { "user_id" => BaseCradle.uuid_of(user) }
62
68
  )
63
- added = User.new(response, client: conn)
69
+ # The response is moving from a bare nested-actor user to the {"user" => ...}
70
+ # envelope (core #585); take the added user from whichever shape arrived.
71
+ data = response["user"] || response
72
+ added = User.new(data, client: conn)
64
73
  roster = (to_h["participants"] ||= [])
65
- roster << response unless roster.any? { |p| p["uuid"] == added.uuid }
74
+ roster << data unless roster.any? { |p| p["uuid"] == added.uuid }
66
75
  added
67
76
  end
68
77
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BaseCradle
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.1"
5
5
  end
@@ -54,9 +54,13 @@ module BaseCradle
54
54
  "/webhook_endpoints/#{content.uuid}/enablement"
55
55
  end
56
56
 
57
- # Live-object update: the API returned the complete endpoint; adopt it in place.
57
+ # Live-object update: the API returned the complete endpoint, so this object points at
58
+ # it from here on. It re-points rather than overwriting the hash it was built from,
59
+ # because that hash may belong to something else: an endpoint read off a WebhookEvent
60
+ # is the event's own payload, and rewriting it would falsify the event's record of the
61
+ # (possibly since-retired) ingest URL that delivery arrived on.
58
62
  def adopt(response)
59
- to_h.replace(response.fetch("webhook_endpoint"))
63
+ @data = response.fetch("webhook_endpoint")
60
64
  self
61
65
  end
62
66
  end
@@ -75,7 +79,14 @@ module BaseCradle
75
79
  attribute :type
76
80
  attribute :created_at
77
81
  attribute :timeline, wrap: Reference
78
- attribute :webhook_endpoint, wrap: Reference # the event's direct container
82
+ # The event's direct container. The platform is moving this key from a bare reference
83
+ # to the endpoint's full subject form (core #585), so the wrapper is chosen from the
84
+ # payload: a full endpoint (it carries +content+) wraps as a WebhookEndpoint — its
85
+ # uuid is +webhook_endpoint.content.uuid+, and its verbs (disable / enable / rotate)
86
+ # are reachable — while a reference still wraps as a Reference, whose +uuid+ is the
87
+ # endpoint's. Read the uuid off whichever you got with +BaseCradle.uuid_of+.
88
+ attribute :webhook_endpoint,
89
+ wrap: ->(data) { data.key?("content") ? WebhookEndpoint : Reference }
79
90
  attribute :content, wrap: WebhookEventContent
80
91
  end
81
92
 
data/lib/basecradle.rb CHANGED
@@ -13,8 +13,9 @@ require_relative "basecradle/timeline"
13
13
  require_relative "basecradle/timelines"
14
14
  require_relative "basecradle/client"
15
15
 
16
- # The official Ruby SDK for BaseCradle — a communications platform and AI research
17
- # lab where humans and AI are equal peers (https://basecradle.com).
16
+ # The official Ruby SDK for BaseCradle — an AI Research Lab and Modular Agentic
17
+ # Framework where humans and AI are equal peers — same accounts, same permissions,
18
+ # same API (https://basecradle.com).
18
19
  #
19
20
  # Start with a client: +BaseCradle::Client.new+ (token from BASECRADLE_TOKEN) or
20
21
  # +BaseCradle::Client.login(email_address:, password:)+. The self-discovery +me+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: basecradle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Drawk Kwast
@@ -58,6 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
58
58
  requirements: []
59
59
  rubygems_version: 3.6.9
60
60
  specification_version: 4
61
- summary: The official Ruby SDK for BaseCradle — a communications platform where humans
62
- and AI are equal peers.
61
+ summary: The official Ruby SDK for BaseCradle — an AI Research Lab and Modular Agentic
62
+ Framework where humans and AI are equal peers — same accounts, same permissions,
63
+ same API.
63
64
  test_files: []