parse-stack-next 5.5.4 → 5.5.6
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 +4 -4
- data/CHANGELOG.md +121 -6
- data/README.md +26 -13
- data/bin/parse-console +9 -1
- data/docs/TEST_SERVER.md +115 -238
- data/docs/mcp_guide.md +1 -1
- data/docs/mongodb_index_optimization_guide.md +3 -2
- data/docs/usage_guide.md +1 -1
- data/docs/yard-template/default/fulldoc/html/css/common.css +52 -9
- data/docs/yard-template/default/fulldoc/html/css/full_list.css +40 -13
- data/lib/parse/agent/constraint_translator.rb +18 -18
- data/lib/parse/agent/errors.rb +29 -7
- data/lib/parse/agent/mcp_rack_app.rb +29 -11
- data/lib/parse/agent/metadata_dsl.rb +6 -6
- data/lib/parse/agent/tools.rb +250 -59
- data/lib/parse/agent.rb +42 -30
- data/lib/parse/api/aggregate.rb +3 -3
- data/lib/parse/api/cloud_functions.rb +19 -10
- data/lib/parse/api/objects.rb +8 -8
- data/lib/parse/api/users.rb +9 -9
- data/lib/parse/atlas_search/session.rb +34 -34
- data/lib/parse/atlas_search.rb +243 -110
- data/lib/parse/client/body_builder.rb +10 -10
- data/lib/parse/client/logging.rb +5 -2
- data/lib/parse/client/profiling.rb +5 -2
- data/lib/parse/client/protocol.rb +1 -1
- data/lib/parse/client/url_redaction.rb +94 -0
- data/lib/parse/client.rb +43 -28
- data/lib/parse/embeddings/image_fetch.rb +6 -1
- data/lib/parse/embeddings/voyage.rb +16 -17
- data/lib/parse/live_query/client.rb +7 -7
- data/lib/parse/live_query/subscription.rb +1 -1
- data/lib/parse/lock.rb +1 -1
- data/lib/parse/lock_backend.rb +118 -2
- data/lib/parse/model/acl.rb +24 -24
- data/lib/parse/model/classes/job_schedule.rb +8 -8
- data/lib/parse/model/classes/job_status.rb +9 -9
- data/lib/parse/model/classes/role.rb +49 -49
- data/lib/parse/model/classes/session.rb +2 -2
- data/lib/parse/model/classes/user.rb +66 -66
- data/lib/parse/model/core/builder.rb +7 -7
- data/lib/parse/model/core/create_lock.rb +1 -1
- data/lib/parse/model/core/properties.rb +4 -4
- data/lib/parse/model/file.rb +57 -16
- data/lib/parse/model/model.rb +19 -19
- data/lib/parse/model/object.rb +38 -38
- data/lib/parse/model/pointer.rb +4 -4
- data/lib/parse/model/push.rb +5 -5
- data/lib/parse/mongodb.rb +84 -26
- data/lib/parse/pipeline_security.rb +2 -2
- data/lib/parse/query/constraints.rb +38 -38
- data/lib/parse/query.rb +151 -75
- data/lib/parse/retrieval/reranker/cohere.rb +30 -0
- data/lib/parse/schema.rb +1 -1
- data/lib/parse/stack/version.rb +1 -1
- data/lib/parse/stack.rb +23 -10
- data/lib/parse/two_factor_auth/user_extension.rb +25 -25
- data/lib/parse/webhooks/payload.rb +35 -35
- data/lib/parse/webhooks/registration.rb +2 -2
- data/lib/parse/webhooks/replay_protection.rb +16 -16
- data/lib/parse/webhooks.rb +11 -11
- data/parse-stack-next.gemspec +19 -1
- metadata +2 -38
- data/.bundle/config +0 -5
- data/.env.sample +0 -138
- data/.env.test +0 -10
- data/.github/ISSUE_TEMPLATE/bug_report.yml +0 -105
- data/.github/ISSUE_TEMPLATE/feature_request.yml +0 -67
- data/.github/dependabot.yml +0 -13
- data/.github/workflows/codeql.yml +0 -44
- data/.github/workflows/docs.yml +0 -39
- data/.github/workflows/release.yml +0 -43
- data/.github/workflows/ruby.yml +0 -38
- data/.gitignore +0 -56
- data/.ruby-version +0 -1
- data/.solargraph.yml +0 -22
- data/.vscode/settings.json +0 -3
- data/.yardopts +0 -19
- data/Gemfile +0 -43
- data/Gemfile.lock +0 -198
- data/Makefile +0 -63
- data/Rakefile +0 -825
- data/config/parse-config.json +0 -12
- data/scripts/debug-ips.js +0 -35
- data/scripts/docker/Dockerfile.parse +0 -17
- data/scripts/docker/atlas-init.js +0 -284
- data/scripts/docker/docker-compose.atlas.yml +0 -80
- data/scripts/docker/docker-compose.test.yml +0 -159
- data/scripts/docker/docker-compose.verifyemail.yml +0 -4
- data/scripts/docker/mongo-init.js +0 -21
- data/scripts/docker/preflight.sh +0 -76
- data/scripts/eval_mcp_with_lm_studio.rb +0 -274
- data/scripts/start-parse.sh +0 -154
- data/scripts/start_mcp_server.rb +0 -78
- data/scripts/test_server_connection.rb +0 -82
- data/scripts/vector_prototype/create_vector_index.js +0 -105
- data/scripts/vector_prototype/fetch_embeddings.py +0 -241
- data/scripts/vector_prototype/fixture_manifest.json +0 -9
- data/scripts/vector_prototype/query_prototype.rb +0 -84
- data/scripts/vector_prototype/run.sh +0 -34
|
@@ -72,9 +72,9 @@ module Parse
|
|
|
72
72
|
].freeze
|
|
73
73
|
|
|
74
74
|
# Operators whose value carries an inner sub-query of the shape
|
|
75
|
-
#
|
|
75
|
+
# `{className:, where:, key:}`. Each must be validated through
|
|
76
76
|
# {Tools.assert_class_accessible!} so the LLM cannot reach into a
|
|
77
|
-
# hidden class via the sub-query, and the inner
|
|
77
|
+
# hidden class via the sub-query, and the inner `where` must be
|
|
78
78
|
# recursively re-translated so blocked operators inside it are
|
|
79
79
|
# also caught.
|
|
80
80
|
CROSS_CLASS_OPERATORS = %w[
|
|
@@ -260,11 +260,11 @@ module Parse
|
|
|
260
260
|
end
|
|
261
261
|
|
|
262
262
|
# Translate the value of a cross-class operator
|
|
263
|
-
# (
|
|
264
|
-
# carries an embedded
|
|
265
|
-
# the active accessibility policy, and an embedded
|
|
263
|
+
# (`$inQuery`/`$notInQuery`/`$select`/`$dontSelect`). The value
|
|
264
|
+
# carries an embedded `className` that must be validated against
|
|
265
|
+
# the active accessibility policy, and an embedded `where` that
|
|
266
266
|
# must be recursively translated so blocked operators (e.g.
|
|
267
|
-
#
|
|
267
|
+
# `$where` nested inside) cannot smuggle through.
|
|
268
268
|
def translate_cross_class_value(op, val, depth:, agent: nil)
|
|
269
269
|
return val unless val.is_a?(Hash)
|
|
270
270
|
val = val.transform_keys(&:to_s)
|
|
@@ -321,20 +321,20 @@ module Parse
|
|
|
321
321
|
translate_value(val, depth: depth, agent: agent)
|
|
322
322
|
end
|
|
323
323
|
|
|
324
|
-
# Validate the owning-object class named by a
|
|
324
|
+
# Validate the owning-object class named by a `$relatedTo` constraint.
|
|
325
325
|
#
|
|
326
|
-
#
|
|
327
|
-
# Unlike
|
|
326
|
+
# `$relatedTo` has the shape `{ object: <Pointer>, key: <relation field> }`.
|
|
327
|
+
# Unlike `$inQuery` / `$select` it carries no `className` / inner `where`,
|
|
328
328
|
# so it is NOT a {CROSS_CLASS_OPERATORS} entry — but it DOES reach across
|
|
329
329
|
# to a second class: the owning object whose relation is being read. Left
|
|
330
330
|
# unvalidated, an agent narrowed to one class (or with a class globally
|
|
331
|
-
#
|
|
332
|
-
# class via the
|
|
333
|
-
# GHSA-wmwx-jr2p-4j4r, where Parse Server's own
|
|
331
|
+
# `agent_hidden`) could still name a relation anchored on an off-allowlist
|
|
332
|
+
# class via the `object` pointer. That is the SDK-surface analog of
|
|
333
|
+
# GHSA-wmwx-jr2p-4j4r, where Parse Server's own `$relatedTo` bypassed the
|
|
334
334
|
# owning object's ACL. Run the owning class through the same accessibility
|
|
335
335
|
# policy as every other cross-class hop, then translate the value normally.
|
|
336
336
|
#
|
|
337
|
-
# Fails closed when the owning class cannot be resolved from
|
|
337
|
+
# Fails closed when the owning class cannot be resolved from `object`: an
|
|
338
338
|
# unresolvable pointer is exactly the shape that would otherwise slip the
|
|
339
339
|
# check, so refuse the constraint rather than skip it.
|
|
340
340
|
def translate_related_to_value(val, depth:, agent: nil)
|
|
@@ -351,10 +351,10 @@ module Parse
|
|
|
351
351
|
translate_value(val, depth: depth, agent: agent)
|
|
352
352
|
end
|
|
353
353
|
|
|
354
|
-
# Extract the Parse class name of a
|
|
355
|
-
# object from its
|
|
356
|
-
# pointer/relation hash (
|
|
357
|
-
# symbol keys), or a storage-form string (
|
|
354
|
+
# Extract the Parse class name of a `$relatedTo` constraint's owning
|
|
355
|
+
# object from its `object` slot, which may be a Parse::Pointer, a Parse
|
|
356
|
+
# pointer/relation hash (`{__type:, className:, objectId:}`, string or
|
|
357
|
+
# symbol keys), or a storage-form string (`"ClassName$objectId"`).
|
|
358
358
|
# Returns nil when no class can be resolved so the caller can fail closed.
|
|
359
359
|
def related_to_owning_class(val)
|
|
360
360
|
return nil unless val.is_a?(Hash)
|
|
@@ -371,7 +371,7 @@ module Parse
|
|
|
371
371
|
end
|
|
372
372
|
|
|
373
373
|
# Hook into the agent-side accessibility check when the agent
|
|
374
|
-
# module is loaded; in pure-unit contexts where
|
|
374
|
+
# module is loaded; in pure-unit contexts where `Parse::Agent::Tools`
|
|
375
375
|
# has not been loaded, default to a no-op rather than raising —
|
|
376
376
|
# the strict check is enforced wherever the agent dispatches.
|
|
377
377
|
def assert_embedded_class_accessible!(op, class_name, agent: nil)
|
data/lib/parse/agent/errors.rb
CHANGED
|
@@ -121,22 +121,22 @@ module Parse
|
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
|
|
124
|
-
# Raised inside the
|
|
125
|
-
#
|
|
126
|
-
#
|
|
127
|
-
#
|
|
124
|
+
# Raised inside the `call_method` tool when the resolved
|
|
125
|
+
# `ClassName.method_name` is excluded by the agent instance's
|
|
126
|
+
# `methods:` filter. The execute() rescue maps this to a
|
|
127
|
+
# `:tool_filtered` error_code so consumers can distinguish "the
|
|
128
128
|
# filter excluded this method" from "this method isn't declared
|
|
129
129
|
# agent-callable" (a Parse::Error) or "the tier doesn't allow it"
|
|
130
|
-
# (a
|
|
130
|
+
# (a `:permission_denied`).
|
|
131
131
|
class MethodFiltered < AgentError; end
|
|
132
132
|
|
|
133
133
|
# Raised at semantic-search dispatch time when at least one class in
|
|
134
|
-
# the model registry declares
|
|
134
|
+
# the model registry declares `agent_tenant_scope` but the class
|
|
135
135
|
# being searched does not. In a tenant-aware deployment an
|
|
136
136
|
# un-scoped searchable surface would let an agent retrieve across
|
|
137
137
|
# tenant boundaries, so the gate is a hard refusal, not a warning.
|
|
138
138
|
# Enforced at dispatch (when all classes are loaded) rather than at
|
|
139
|
-
#
|
|
139
|
+
# `agent_searchable` declaration time so class-load order can't
|
|
140
140
|
# produce a false negative.
|
|
141
141
|
class MissingTenantScope < AgentError; end
|
|
142
142
|
|
|
@@ -145,5 +145,27 @@ module Parse
|
|
|
145
145
|
# `:refuse`. A SecurityError subclass so it routes through execute's
|
|
146
146
|
# security rescue and is never swallowed.
|
|
147
147
|
class PromptInjectionDetected < SecurityError; end
|
|
148
|
+
|
|
149
|
+
# Raised by Parse::Agent::Tools.invoke when a tool name is recognized
|
|
150
|
+
# (advertised in a permission tier / passes the gates) but has no
|
|
151
|
+
# handler in this SDK version. The built-in raw-CRUD write/admin tools
|
|
152
|
+
# (`create_object`, `update_object`, `delete_object`, `create_class`,
|
|
153
|
+
# `delete_class`) are declared in the write/admin tiers but ship
|
|
154
|
+
# without an implementation — invoking one previously raised a bare
|
|
155
|
+
# `NoMethodError` that collapsed to an opaque "internal error" on the
|
|
156
|
+
# wire. This turns that into a clear, typed refusal so the caller
|
|
157
|
+
# learns the tool is not implemented rather than that the SDK broke.
|
|
158
|
+
class NotImplemented < AgentError
|
|
159
|
+
attr_reader :tool_name
|
|
160
|
+
|
|
161
|
+
def initialize(tool_name, message = nil)
|
|
162
|
+
@tool_name = tool_name.to_s
|
|
163
|
+
super(message || "Tool '#{@tool_name}' is recognized but not implemented in this " \
|
|
164
|
+
"SDK version. The built-in raw create/update/delete tools are not " \
|
|
165
|
+
"available; expose the operation as an application method via " \
|
|
166
|
+
"`agent_method` + `call_method`, or register a handler with " \
|
|
167
|
+
"`Parse::Agent::Tools.register`.")
|
|
168
|
+
end
|
|
169
|
+
end
|
|
148
170
|
end
|
|
149
171
|
end
|
|
@@ -1183,10 +1183,21 @@ module Parse
|
|
|
1183
1183
|
# Wire format for each SSE event (note: trailing blank line is required
|
|
1184
1184
|
# by the SSE spec):
|
|
1185
1185
|
#
|
|
1186
|
-
# event:
|
|
1186
|
+
# event: message\n
|
|
1187
1187
|
# data: <json>\n
|
|
1188
1188
|
# \n
|
|
1189
1189
|
#
|
|
1190
|
+
# EVERY frame — progress notifications, list-changed notifications,
|
|
1191
|
+
# and the final JSON-RPC response alike — carries the event name
|
|
1192
|
+
# `message`. MCP Streamable HTTP defines exactly one SSE event type
|
|
1193
|
+
# for JSON-RPC traffic; clients discriminate by inspecting the
|
|
1194
|
+
# envelope (`method` present => notification, `id` + `result`/`error`
|
|
1195
|
+
# => response), NOT by the SSE event name. Earlier releases emitted
|
|
1196
|
+
# `event: progress` and `event: response`, which real MCP clients
|
|
1197
|
+
# silently discard — they match only the default `message` type — so
|
|
1198
|
+
# the final response never arrived and the call appeared to hang.
|
|
1199
|
+
# Do not reintroduce custom event names.
|
|
1200
|
+
#
|
|
1190
1201
|
# @api private
|
|
1191
1202
|
class SSEBody
|
|
1192
1203
|
# Sentinel pushed to the queue when the worker is done.
|
|
@@ -1546,6 +1557,9 @@ module Parse
|
|
|
1546
1557
|
# The `total` field is omitted (rather than nil) so the wire
|
|
1547
1558
|
# shape matches the spec's optional-field convention.
|
|
1548
1559
|
#
|
|
1560
|
+
# Emitted as `event: message` — see the SSEBody class docs. The
|
|
1561
|
+
# payload's `method` is what marks it as progress.
|
|
1562
|
+
#
|
|
1549
1563
|
# @param elapsed [Float] seconds elapsed since the stream started.
|
|
1550
1564
|
# @return [String] SSE event string (includes trailing blank line).
|
|
1551
1565
|
def build_progress_event(elapsed)
|
|
@@ -1557,15 +1571,14 @@ module Parse
|
|
|
1557
1571
|
"progress" => elapsed,
|
|
1558
1572
|
},
|
|
1559
1573
|
})
|
|
1560
|
-
"event:
|
|
1574
|
+
"event: message\ndata: #{data}\n\n"
|
|
1561
1575
|
end
|
|
1562
1576
|
|
|
1563
1577
|
# Format a `notifications/tools/list_changed` or
|
|
1564
1578
|
# `notifications/prompts/list_changed` SSE event. Both
|
|
1565
1579
|
# notifications have no `params` — the wire shape is just the
|
|
1566
|
-
# JSON-RPC envelope with `method` set.
|
|
1567
|
-
#
|
|
1568
|
-
# progress event name is reserved for progress notifications).
|
|
1580
|
+
# JSON-RPC envelope with `method` set. Emitted as `event: message`,
|
|
1581
|
+
# like every other frame on this stream.
|
|
1569
1582
|
#
|
|
1570
1583
|
# @param method [String] full MCP method string.
|
|
1571
1584
|
# @return [String] SSE event string (includes trailing blank line).
|
|
@@ -1599,7 +1612,7 @@ module Parse
|
|
|
1599
1612
|
"method" => "notifications/progress",
|
|
1600
1613
|
"params" => params,
|
|
1601
1614
|
})
|
|
1602
|
-
"event:
|
|
1615
|
+
"event: message\ndata: #{data}\n\n"
|
|
1603
1616
|
end
|
|
1604
1617
|
|
|
1605
1618
|
# Build the callback the dispatcher block passes into
|
|
@@ -1631,12 +1644,17 @@ module Parse
|
|
|
1631
1644
|
end
|
|
1632
1645
|
end
|
|
1633
1646
|
|
|
1634
|
-
# Format the final
|
|
1647
|
+
# Format the final JSON-RPC response SSE event.
|
|
1648
|
+
#
|
|
1649
|
+
# Emitted as `event: message` (NOT `event: response`) — an MCP
|
|
1650
|
+
# client matching only the default `message` type would otherwise
|
|
1651
|
+
# discard the response and block until its own timeout. The
|
|
1652
|
+
# envelope's `id` + `result`/`error` is what marks it final.
|
|
1635
1653
|
#
|
|
1636
1654
|
# @param body [Hash] JSON-RPC response envelope.
|
|
1637
1655
|
# @return [String] SSE event string (includes trailing blank line).
|
|
1638
1656
|
def build_response_event(body)
|
|
1639
|
-
"event:
|
|
1657
|
+
"event: message\ndata: #{JSON.generate(body)}\n\n"
|
|
1640
1658
|
end
|
|
1641
1659
|
|
|
1642
1660
|
# Build an internal-error JSON-RPC envelope (id may be nil at this layer).
|
|
@@ -1755,9 +1773,9 @@ module Parse
|
|
|
1755
1773
|
end
|
|
1756
1774
|
end
|
|
1757
1775
|
|
|
1758
|
-
# SSE wire form for a server→client notification. Event name
|
|
1759
|
-
#
|
|
1760
|
-
# SSE path
|
|
1776
|
+
# SSE wire form for a server→client notification. Event name
|
|
1777
|
+
# "message" — the single event type MCP Streamable HTTP defines for
|
|
1778
|
+
# JSON-RPC traffic, matching the request-scoped SSE path.
|
|
1761
1779
|
def format_event(notification)
|
|
1762
1780
|
"event: message\ndata: #{JSON.generate(notification)}\n\n"
|
|
1763
1781
|
end
|
|
@@ -450,15 +450,15 @@ module Parse
|
|
|
450
450
|
# preview-only execution. When false (default), passing dry_run: true in
|
|
451
451
|
# arguments is refused at dispatch time with :invalid_argument.
|
|
452
452
|
# @param permitted_keys [Array<Symbol,String>, nil] when provided,
|
|
453
|
-
#
|
|
453
|
+
# `call_method` refuses any `arguments` key not in this list.
|
|
454
454
|
# Without this, an LLM (or a prompt-injection payload) can
|
|
455
|
-
# pass arbitrary keys through a method that splats with
|
|
456
|
-
# reaching protected columns like
|
|
457
|
-
# Highly recommended on any
|
|
455
|
+
# pass arbitrary keys through a method that splats with `**`,
|
|
456
|
+
# reaching protected columns like `_hashed_password` or `ACL`.
|
|
457
|
+
# Highly recommended on any `agent_write`/`agent_admin` method
|
|
458
458
|
# that takes a kwargs splat.
|
|
459
459
|
# @param parameters [Hash, nil] when provided, a JSON Schema (as a
|
|
460
|
-
# Ruby Hash) describing the
|
|
461
|
-
#
|
|
460
|
+
# Ruby Hash) describing the `arguments` object. Surfaced in
|
|
461
|
+
# `tools/list` so the LLM submits properly-shaped inputs and
|
|
462
462
|
# stricter MCP clients can validate before dispatch.
|
|
463
463
|
# @return [Hash] the method metadata
|
|
464
464
|
def agent_method(method_name, description = nil, permission: :readonly,
|