Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
16aa7ef
wave6: retire dead ctor entries (ALLOWLIST_DISCIPLINE §495, shared-di…
mjerris Jul 27, 2026
9dbbd25
derived-attr parity: expose SWMLService TLS config + SpiderSkill remo…
mjerris Jul 27, 2026
30050b5
chore(surface): regenerate port_surface.json — was stale at c72a0b2
mjerris Jul 27, 2026
88253c6
enumerator: lift `include`d module members onto the including class
mjerris Jul 27, 2026
07eda64
fix(secure-default): emit the rendered wire payload, not a self-verdict
mjerris Jul 27, 2026
9fb26e2
fix(security): SessionManager default token_expiry_secs 3600 -> 900
mjerris Jul 28, 2026
1fa058c
enumerator: recover parameter DEFAULT VALUES from source (0 -> 330)
mjerris Jul 28, 2026
d712476
fix(enumerator): Integer()/Float() are methods, not callable objects
mjerris Jul 28, 2026
c5daf0c
fold(web): WebService.start host default 127.0.0.1 -> 0.0.0.0 (refere…
mjerris Jul 28, 2026
c36ddd0
fix(parity): burn required-flip/default-invented/default-mismatch to …
mjerris Jul 28, 2026
ea630da
fold(skills): rename SkillRegistry.get_factory -> get_skill_class in …
mjerris Jul 28, 2026
3245fae
fix(parity): pin the 7 wire-key remaps; route the nested credential S…
mjerris Jul 28, 2026
3e72486
fix(surface): a Struct's inherited constructor satisfies the oracle's…
mjerris Jul 28, 2026
7de2304
chore: declare version 3.0.0
mjerris Jul 28, 2026
2b6290b
chore(release): set the SemVer floor to 3.0.0 and re-anchor its surfa…
mjerris Jul 28, 2026
037b395
fix(security): compare the auth-scheme case-insensitively and require…
mjerris Jul 28, 2026
1dd8aa0
chore: consolidate the never-published CHANGELOG entries into one 3.0.0
mjerris Jul 28, 2026
65ca770
docs: add .version-frozen — version and SemVer floor pinned at 3.0.0
mjerris Jul 28, 2026
3e50a49
fix(security): mint SWAIG tool tokens with PADDED urlsafe base64 + wi…
mjerris Jul 28, 2026
48bd4fe
chore(gate): re-baseline the TYPE-EROSION ratchet onto the corrected …
mjerris Jul 29, 2026
d6a1261
fix(logging): put the control-char scrub ON the emission path
mjerris Jul 29, 2026
654c2ca
fix(logging): drop the port-only strip_control_chars_value; route the…
mjerris Jul 29, 2026
1228980
docs: finish c36ddd0's required-handler flip in docs/, relay/ and the…
mjerris Jul 29, 2026
45d7cc1
fix(ledger): delete 2 false signature omissions — neither divergence …
mjerris Jul 29, 2026
8b1a667
docs(claude): fix the define_tool example — it raised ArgumentError a…
mjerris Jul 29, 2026
dde0516
ci(gates): schedule TLS-VERIFY — defined, passing, never run
mjerris Jul 29, 2026
aa9f284
chore(doc-surface): re-pin floor 51.8 -> 52.4 after the gate stopped …
mjerris Jul 29, 2026
5bce988
docs(gen): emit a doc comment on every nested namespace module
mjerris Jul 29, 2026
859c863
docs(relay,contexts): document relay_event.rb + context_builder.rb
mjerris Jul 29, 2026
21f9053
docs(agent): document agent_base.rb (109 decls)
mjerris Jul 29, 2026
cbb9be5
docs(relay): document action.rb + client.rb (81 decls)
mjerris Jul 29, 2026
5988717
docs(lib): document the namespace modules + the SkillBase contract ov…
mjerris Jul 29, 2026
9840e4d
docs(rest,relay,core,web): http_client, call, security_config, web_se…
mjerris Jul 29, 2026
2cdd923
docs(skills): math, google_maps, mcp_gateway, info_gatherer (65 decls)
mjerris Jul 29, 2026
2010a9c
docs(core,serverless,utils,swaig): auth_handler, lambda_handler, sche…
mjerris Jul 29, 2026
f5ff9e8
docs(skills): the remaining 14 builtin skills (118 decls)
mjerris Jul 29, 2026
a9db4e2
docs(lib): document the last 147 declarations — DOC-SURFACE reaches 100%
mjerris Jul 29, 2026
60cb67f
chore(doc-surface): pin the floor at 100.0%
mjerris Jul 29, 2026
a19ffaa
ci(doc-surface): flip from report-only to BLOCKING and drop the fail-…
mjerris Jul 29, 2026
c54fede
fix(swml): validate post_prompt shape — the validator passed configs …
mjerris Jul 30, 2026
91c5ba3
fix(datamap): drop `body` from create_simple_api_tool — the key is in…
mjerris Jul 30, 2026
c42bb3d
feat(datamap)!: remove DataMap#body — the builder wrote a key nothing…
mjerris Jul 30, 2026
3a86acc
fix(swml): `play` has no `text` key — emit `say:` URL through the val…
mjerris Jul 30, 2026
3809cf5
fix(swml): honor `x-sdk-widen` — the validator was stricter than the …
mjerris Jul 30, 2026
54b1494
fix(swml): vendored schema was missing `AIObject.multilingual`
mjerris Jul 30, 2026
db8c136
fix(swml): route every verb emission through the validating add_verb
mjerris Jul 30, 2026
52f4503
fix(bedrock): validate the post-render verb rewrite
mjerris Jul 30, 2026
8cf87e2
fix: keep the new validation helper off the public surface; de-jargon…
mjerris Jul 30, 2026
ff40180
Revert "fix(swml): vendored schema was missing `AIObject.multilingual`"
mjerris Jul 30, 2026
e710b6c
docs(tools): de-jargon the SWAIG tool registry's public YARD docs
mjerris Jul 30, 2026
9da505f
docs(tools): drop "Mirrors Python's ..." from the type-inference docs
mjerris Jul 30, 2026
2e637c1
fix(security)!: SWAIG `secure` tools failed OPEN on every transport
mjerris Jul 30, 2026
b6290e6
test(http-dump): implement the pending serverless valid-token fixture
mjerris Jul 30, 2026
2d9190d
ci: wire the SIGNATURES-FRESH gate
mjerris Jul 30, 2026
e8ce35b
docs(generated): state what a generated DTO IS, not how it was ported
mjerris Jul 30, 2026
e9161df
docs: drop cross-language porting framing from the shipped public docs
mjerris Jul 30, 2026
4c98046
lint: remove the directory carve-outs so examples/ + bin/*-dump are l…
mjerris Jul 30, 2026
2a6a161
lint: burn the mechanical rubocop findings in examples/ + bin/*-dump …
mjerris Jul 30, 2026
30b48ab
lint(examples): decompose skills_audit_harness's DataMap path
mjerris Jul 30, 2026
3bc703a
lint(examples): clear the 13 Layout/LineLength offenses (13 -> 0)
mjerris Jul 30, 2026
671802c
lint: clear the last non-Metrics offenses in bin/*-dump + examples/
mjerris Jul 30, 2026
e0e13ad
lint(bin): decompose swml-dump / wait-liveness-dump / secret-scrub-dump
mjerris Jul 30, 2026
e6c4ca6
lint(bin): split relay-liveness-dump's transport from its fault policy
mjerris Jul 30, 2026
844ef96
lint(bin): finish the Metrics burn — bin/ is now rubocop-clean
mjerris Jul 30, 2026
c15f3b1
fix(examples): relay_ivr_connect crashed on its first play_and_collect
mjerris Jul 30, 2026
9b4c2e4
lint: burn the last Metrics offenses in examples/ + doc_wire_runner (…
mjerris Jul 30, 2026
5414292
lint(tests): extract the self-signed-cert builder instead of disablin…
mjerris Jul 30, 2026
067ca53
fix(scripts): signature_dump silently shortened its default index on …
mjerris Jul 30, 2026
b195700
lint(python): add a ruff config and burn scripts/*.py to zero (58 -> 0)
mjerris Jul 30, 2026
cfbe097
ci: wire PY-LINT + PY-FMT; document the widened FMT/LINT scope
mjerris Jul 30, 2026
e99b963
fix: move ruff.toml to eng/ (ROOT-HYGIENE) and resync the README incl…
mjerris Jul 30, 2026
895087f
test(relay): stop mutating process-global ENV from parallel suites
mjerris Jul 30, 2026
2dbcb14
fold(surface): PromptMixin.contexts is a rename, not an omission
mjerris Jul 30, 2026
eb7ea53
ledger(surface): delete 9 self-refuting "reference-oracle gap" omissions
mjerris Jul 30, 2026
fc87320
fold(enumerator): mirror the skills instance_key alias into the signa…
mjerris Jul 30, 2026
ada5e3e
test(tls): prove an https/wss client never silently downgrades to cle…
mjerris Jul 30, 2026
01c0c7c
fold(enumerators): gate the skill-hook surface on the LIVE oracle, bo…
mjerris Jul 30, 2026
b1a5b02
docs(gen-relay): correct the stale placeholder arithmetic; record cal…
mjerris Jul 30, 2026
66f5307
regen(swaig): refresh the SWAIG payloads the spec re-vendors staled
mjerris Aug 4, 2026
2142491
ruby: pin rubocop to a minor version so local FMT/LINT reproduces CI
mjerris Aug 4, 2026
76ff2f2
ci: pin ruff (was 4x unbounded) + assert the pin at gate time
mjerris Aug 4, 2026
4d38e24
ci: hint the PINNED ruff install on the not-found path
mjerris Aug 4, 2026
2abc8eb
gen(swaig): emit the SwaigAction/SwaigResponse envelope the cross-fil…
mjerris Aug 5, 2026
25202a1
gen(relay): read the combined spec through the shared adapter (R11)
mjerris Aug 6, 2026
57f4945
gen(swaig): regenerate SWAIG payloads after the spec re-vendor
mjerris Aug 7, 2026
9b2b75c
gen(signatures): regenerate port_signatures.json after the SWAIG payl…
mjerris Aug 7, 2026
c606d77
fix(schema): stop a validator build failure silently disabling verb v…
mjerris Aug 10, 2026
e9c782d
fix(enumerator): a usage error must not share an exit code with a ver…
mjerris Aug 10, 2026
2076ebf
fix(swml): resolve anyOf/oneOf branches so the closed-key check stays…
mjerris Aug 10, 2026
4e46147
docs(enumerator): record the porting-sdk provenance of the synthesize…
mjerris Aug 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .doc_surface_floor
Original file line number Diff line number Diff line change
@@ -1 +1 @@
51.8
100.0
36 changes: 36 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,24 @@ jobs:
pip install -e porting-sdk/test_harness/mock_relay
pip install -e porting-sdk/test_harness/mock_signalwire

# ruff backs the PY-LINT / PY-FMT gates over the hand-written Python in
# scripts/ (the 5 code generators, the surface enumerator, _gen_format.py).
# scripts/_env.sh fails LOUD when ruff is absent rather than skipping, so
# the gate cannot pass vacuously on a runner that lacks it.
#
# VERSION PINNED, in lockstep with SW_RUFF_VERSION in scripts/_env.sh.
# A bare `pip install ruff` resolved the newest release at CI time while a
# contributor ran whatever they installed months ago, so a ruff that adds a
# rule or changes a format heuristic reds PY-LINT/PY-FMT on code that never
# changed — and no local run reproduces it. _env.sh asserts the same version
# at gate time, so a pin that silently did not take is caught rather than
# quietly producing a different verdict.
- name: Install ruff (PY-LINT / PY-FMT gates)
run: |
pip install ruff==0.15.21
ruff --version
ruff --version | grep -qw '0\.15\.21' || { echo "FATAL: ruff is not the pinned 0.15.21" >&2; exit 1; }

# EMISSION needs `import signalwire` (the oracle). Install from the
# adjacent checkout only if not already importable.
- name: Install signalwire-python (EMISSION oracle) if not importable
Expand Down Expand Up @@ -197,6 +215,24 @@ jobs:
pip install -e porting-sdk/test_harness/mock_relay
pip install -e porting-sdk/test_harness/mock_signalwire

# ruff backs the PY-LINT / PY-FMT gates over the hand-written Python in
# scripts/ (the 5 code generators, the surface enumerator, _gen_format.py).
# scripts/_env.sh fails LOUD when ruff is absent rather than skipping, so
# the gate cannot pass vacuously on a runner that lacks it.
#
# VERSION PINNED, in lockstep with SW_RUFF_VERSION in scripts/_env.sh.
# A bare `pip install ruff` resolved the newest release at CI time while a
# contributor ran whatever they installed months ago, so a ruff that adds a
# rule or changes a format heuristic reds PY-LINT/PY-FMT on code that never
# changed — and no local run reproduces it. _env.sh asserts the same version
# at gate time, so a pin that silently did not take is caught rather than
# quietly producing a different verdict.
- name: Install ruff (PY-LINT / PY-FMT gates)
run: |
pip install ruff==0.15.21
ruff --version
ruff --version | grep -qw '0\.15\.21' || { echo "FATAL: ruff is not the pinned 0.15.21" >&2; exit 1; }

# A clean-bundle require + the full suite on the forward Ruby: -w surfaces any
# deprecation warning from a stdlib gem that was extracted from the defaults.
- name: Require smoke + gemspec-dependency honesty (extracted-stdlib deps)
Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,25 @@ jobs:
pip install -e porting-sdk/test_harness/mock_relay
pip install -e porting-sdk/test_harness/mock_signalwire

# ruff backs the PY-LINT / PY-FMT gates over the hand-written Python in
# scripts/ (the 5 code generators, the surface enumerator, _gen_format.py).
# scripts/_env.sh fails LOUD when ruff is absent rather than skipping, so
# the gate cannot pass vacuously on a runner that lacks it.
#
# VERSION PINNED, in lockstep with SW_RUFF_VERSION in scripts/_env.sh.
# A bare `pip install ruff` resolved the newest release at CI time while a
# contributor ran whatever they installed months ago, so a ruff that adds a
# rule or changes a format heuristic reds PY-LINT/PY-FMT on code that never
# changed — and no local run reproduces it. That matters most HERE: this
# workflow gates a RELEASE, so a floating linter could block a publish on a
# tag whose tree was green when it was cut. _env.sh asserts the same version
# at gate time, so a pin that silently did not take is caught.
- name: Install ruff (PY-LINT / PY-FMT gates)
run: |
pip install ruff==0.15.21
ruff --version
ruff --version | grep -qw '0\.15\.21' || { echo "FATAL: ruff is not the pinned 0.15.21" >&2; exit 1; }

- name: Run CI gate script (full run-ci.sh — the same gate set as PR CI)
working-directory: signalwire-ruby
env:
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,29 @@ jobs:
pip install -e porting-sdk/test_harness/mock_relay
pip install -e porting-sdk/test_harness/mock_signalwire

# ruff backs the PY-LINT / PY-FMT gates over the hand-written Python in
# scripts/ (the 5 code generators, the surface enumerator, _gen_format.py).
# scripts/_env.sh fails LOUD when ruff is absent rather than skipping, so
# the gate cannot pass vacuously on a runner that lacks it — hence this
# explicit install rather than relying on it being preinstalled.
#
# VERSION PINNED, in lockstep with SW_RUFF_VERSION in scripts/_env.sh.
# A bare `pip install ruff` resolved the newest release at CI time while a
# contributor ran whatever they installed months ago, so a ruff that adds a
# rule or changes a format heuristic reds PY-LINT/PY-FMT on code that never
# changed — and no local run reproduces it. This repo already paid for that
# shape through rubocop: an open floor ('>= 1.80') let CI resolve 1.89.0
# against a local 1.88.0, and 1.89's tightened
# Layout/MultilineMethodCallIndentation failed CI with 3 offenses on a
# commit whose local run reported "no offenses detected". _env.sh asserts
# the same version at gate time, so a pin that silently did not take is
# caught rather than quietly producing a different verdict.
- name: Install ruff (PY-LINT / PY-FMT gates)
run: |
pip install ruff==0.15.21
ruff --version
ruff --version | grep -qw '0\.15\.21' || { echo "FATAL: ruff is not the pinned 0.15.21" >&2; exit 1; }

# EMISSION needs `import signalwire` (the oracle). Install from the
# adjacent checkout only if not already importable.
- name: Install signalwire-python (EMISSION oracle) if not importable
Expand Down
67 changes: 40 additions & 27 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,15 @@ AllCops:
TargetRubyVersion: 3.2
SuggestExtensions: false
Exclude:
# Demo programs, not library surface — excluded from the blocking gate the way
# signalwire-go tags its examples //go:build ignore. They are not part of the
# audited public surface and are free to read as tutorial-style code.
- 'examples/**/*'
- 'relay/examples/**/*'
- 'rest/examples/**/*'
# THIRD-PARTY code we do not own. This is the ONLY directory-level carve-out.
# Everything else in this repo — lib/, tests/, examples/, relay/examples/,
# rest/examples/, bin/ (including the *-dump programs), scripts/ — is linted
# and formatted at the SAME bar as the shipped library. Owner ruling
# 2026-07-30: "examples and tests are shipping code too ... all at the levels
# the shipping code gets." The prior "tutorial-style / not audited public
# surface" rationale was about PUBLIC-SURFACE AUDITING, not about whether the
# code should be well-formed; it is not a reason to hold it below the bar.
- 'vendor/**/*'
# Layer-D cross-port dump programs (bin/*-dump) — the per-surface behavioral
# differ tooling, mirroring the Go reference's cmd/*-dump. Tutorial-style
# port-native dispatch programs, not audited library surface; same rationale
# as examples/ above and go's //go:build ignore dumps.
- 'bin/wire-dump'
- 'bin/swml-dump'
- 'bin/state-dump'
- 'bin/http-dump'
- 'bin/envelope-dump'
- 'bin/wire-relay-dump'
- 'bin/wait-liveness-dump'
- 'bin/secure-default-dump'
- 'bin/relay-liveness-dump'
- 'bin/pagination-dump'
- 'bin/secret-scrub-dump'
- 'bin/ai-chat-dump'
# Cross-port gate fixture-replay tooling, same category as the bin/*-dump
# programs above: doc_wire_runner.rb only DRIVES the documented REST calls
# against the DOC-WIRE flag-mode mock so it journals the wire shape. Not
# audited library surface.
- 'scripts/doc_wire_runner.rb'
# NOTE: the machine-generated trees (REST resource layer, READ-SIDE typed
# payload trees, and the full-mock REST wire-test suite) are DELIBERATELY NOT
# excluded here — generated code is linted like hand-written code so a
Expand Down Expand Up @@ -195,6 +176,38 @@ Metrics/ModuleLength:
Metrics/MethodLength:
Exclude:
- 'lib/signalwire/skills/builtin/mcp_gateway.rb'
# signature_dump.rb's Ripper default-value extractor: same DATA-SHAPE (not
# logic) rationale. Its size is a 1:1 DISPATCH over Ruby's literal AST node
# types (int/float/keyword/string/symbol/array/hash), one arm per node kind
# the parser can emit. The arms were already extracted into named
# per-node-type helpers (ripper_string_literal / ripper_array_literal /
# ripper_hash_literal / ...); what remains is the irreducible dispatch itself
# plus each helper's exhaustive shape-guard chain. Splitting further would
# scatter one node type's handling across two methods and make it HARDER to
# confirm the extractor rejects every non-literal — which is the property
# that keeps it from emitting a fabricated default. Size only; no naming or
# type idiom is being suppressed.
- 'scripts/signature_dump.rb'

# Same scope + same rationale as the Metrics/MethodLength entry above, for the
# same file. A literal-AST extractor's "complexity" is entirely SHAPE-GUARDING:
# every branch asks "is this node actually the literal it looks like?" (is it an
# Array, is its head the expected tag, does a string contain an interpolation,
# is every array element itself a literal). Those guards are the correctness
# property — each one is a place the extractor declines to invent a value it
# cannot prove — so collapsing them to satisfy a branch count would trade the
# gate's honesty for a metric. Scoped to this file only.
Metrics/CyclomaticComplexity:
Exclude:
- 'scripts/signature_dump.rb'

Metrics/PerceivedComplexity:
Exclude:
- 'scripts/signature_dump.rb'

Metrics/AbcSize:
Exclude:
- 'scripts/signature_dump.rb'

# Prefer keyword args for boolean defaults — a good cop in general, but a known
# false positive on FLUENT SINGLE-ARGUMENT TOGGLES, which is exactly these sites:
Expand Down
38 changes: 38 additions & 0 deletions .version-frozen
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# VERSION FREEZE — TEMPORARY. DELETE THIS FILE BEFORE RELEASING.
#
# Ruled by the owner 2026-07-28. This port declares version 3.0.0, and its SemVer
# release floor (port_signatures.baseline.json -> baseline_version) is pinned at
# 3.0.0 with the recorded surface payload overwritten to match today's surface.
#
# THIS FILE IS DOCUMENTATION ONLY (owner ruling, 2026-07-28). No gate reads it and
# nothing fails if it is present. It exists so that neither a person nor a future
# session has to re-derive why the versions look the way they do — and so nobody
# casually bumps one.
#
# WHY THE FREEZE: versions had drifted to 3.0.2 / 3.2.0 / 3.2.1 / 3.3.0 / 4.0.0
# across the fleet with NOTHING EVER PUBLISHED above v2.x. `git ls-remote --tags
# origin` tops out at v1.1.2 for rust/dotnet and v2.0.x for most others; php's
# v3.2.0 tag is local-only, zero remote matches. That scatter was never a release
# history, so collapsing it rewrote nothing real.
#
# WHILE THIS FILE EXISTS:
# - do NOT bump this port's version
# - do NOT re-anchor its release floor
# - DO land surface changes freely. The floor is a snapshot of the 3.0.0 wave,
# not of a published release, so SEMVER-DIFF will not flag anything already in
# today's surface. Anything added AFTER the freeze is still caught normally.
#
# BEFORE CUTTING A RELEASE:
# 1. DELETE this file in every port.
# 2. Re-anchor each port_signatures.baseline.json to the actual released
# commit/tag and regenerate its surface payload from that point.
# 3. Re-enable enforcement: ports invoke SEMVER-DIFF with --report-only today
# (the D5 "re-anchor at cut" setting). Dropping that flag turns semver
# enforcement back on.
#
# NOTE: perl is deliberately NOT floor-pinned. It is the only port anchored to a
# genuinely published tag (v2.0.2, sha 83376ff) and already passes at 3.0.0;
# overwriting it would destroy the fleet's only real release anchor.
#
# Owner rulings: porting-sdk tasks #143 (version) and #146 (floor).
# Full context: porting-sdk/SESSION_HANDOFF_2026-07-28.md
32 changes: 14 additions & 18 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,28 @@
All notable changes to the SignalWire AI Agents SDK for Ruby (`signalwire-sdk`)
are documented here. This project adheres to [Semantic Versioning](https://semver.org).

## 3.2.0
## 3.0.0

### Added
- `client.messages` — the new `/api/messaging/messages` send/redact REST
resource (`create` to send an SMS/MMS message, `update` to redact a message
body). Distinct from the `client.logs.messages` message-log namespace.
Generated from the canonical `messages` REST spec with full success + error
wire-test coverage.

## 3.1.0
Parity release aligning the Ruby SDK with the Python reference SDK across the
REST, RELAY, SWML, and SWAIG surfaces, and the fleet-wide convergence version:
every SignalWire SDK port declares 3.0.0. Nothing in the 3.x range was ever
published — this port's published tags top out at `v2.0.0` — so the
previously-drafted unreleased 3.0.2 / 3.1.0 / 3.2.0 entries are consolidated
here rather than shipped as a staggered series.

### Added
- Spec-generated REST surface: a typed REST client with generated typed I/O and
full success + error wire-test coverage across the SignalWire REST namespaces.
- `client.projects` — the new `/api/projects` full-CRUD project-management REST
resource (list / create / get / update / delete subprojects, plus
`rotate_signing_key`). Distinct from the singular `client.project` token
namespace. Generated from the canonical `projects` REST spec with full
success + error wire-test coverage.

## 3.0.2

Parity release aligning the Ruby SDK with the Python reference SDK across the
REST, RELAY, SWML, and SWAIG surfaces.

### Added
- Spec-generated REST surface: a typed REST client with generated typed I/O and
full success + error wire-test coverage across the SignalWire REST namespaces.
- `client.messages` — the new `/api/messaging/messages` send/redact REST
resource (`create` to send an SMS/MMS message, `update` to redact a message
body). Distinct from the `client.logs.messages` message-log namespace.
Generated from the canonical `messages` REST spec with full success + error
wire-test coverage.
- `paginate()` parity on read resources via a `PaginatedIterator`.
- Typed SWAIG `ParameterSchema` builder for `define_tool`, and typed RELAY state
enums (`Device`, `CollectConfig`, record/tap direction/format/codec, skill names).
Expand Down
5 changes: 3 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ ruby bin/sw-search ./docs --output knowledge.swsearch
#### Agent Creation
Agents inherit from `SignalWire::AgentBase` and define:
- Prompt Object Model (POM) sections via `prompt_add_section`
- SWAIG functions via `define_tool` with block handlers
- SWAIG functions via `define_tool` (`handler:` is a required keyword; pass `handler: nil` to use a block)
- Skills integration via `add_skill` method
- All configuration methods return `self` for chaining

Expand All @@ -97,7 +97,8 @@ agent.define_tool(
description: 'Get current weather',
parameters: {
'city' => { 'type' => 'string', 'description' => 'City name' }
}
},
handler: nil
) do |args, raw_data|
SignalWire::Swaig::FunctionResult.new("Weather in #{args['city']}: 72F")
end
Expand Down
15 changes: 12 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,16 @@ group :development, :test do
gem 'rack-test', '>= 2.0'
gem 'rake', '>= 13.0'
# Lint/format quality floor (FMT + LINT gates in scripts/run-ci.sh).
gem 'rubocop', '>= 1.80'
gem 'rubocop-minitest', '>= 0.38'
gem 'rubocop-performance', '>= 1.25'
#
# These carry an upper bound on the MINOR version, not just a floor. With an
# open floor ('>= 1.80'), CI resolves the newest release while local runs use
# the committed Gemfile.lock, so a rubocop release that adds or tightens a cop
# turns CI red with no code change and local FMT/LINT cannot reproduce it.
# That happened with 1.89.0, which tightened
# Layout/MultilineMethodCallIndentation: local 1.88.0 reported 0 offenses on
# the exact commit CI failed with 3. Bump these deliberately (and fix the new
# offenses in the same commit) rather than letting CI float.
gem 'rubocop', '>= 1.89', '< 1.90'
gem 'rubocop-minitest', '>= 0.40', '< 0.41'
gem 'rubocop-performance', '>= 1.26', '< 1.27'
end
Loading
Loading