insightfactory-cli 1.1.2.dev29__py3-none-any.whl → 1.1.2.dev30__py3-none-any.whl

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.
if_cli/router/upstream.py CHANGED
@@ -138,6 +138,22 @@ class ConnectUrlRecorder:
138
138
  return types.ElicitResult(action="decline")
139
139
 
140
140
 
141
+ def _has_url_request(result: types.InputRequiredResult) -> bool:
142
+ """Whether any of the result's requests is the Databricks connect flow.
143
+
144
+ A request in any other mode (today, only `form`) gates a preview behind a
145
+ confirmation rather than asking for a trip to a browser, so its presence
146
+ alone never puts the result on the connect path. A URL-mode request does,
147
+ even one whose URL fails the origin check in `_connect_url` and so
148
+ contributes no link: the call still did not run, and the caller still
149
+ needs the existing refusal, not a result that reads as success.
150
+ """
151
+ return any(
152
+ isinstance(getattr(request, "params", None), types.ElicitRequestURLParams)
153
+ for request in (result.input_requests or {}).values()
154
+ )
155
+
156
+
141
157
  def _relayable_requests(result: types.InputRequiredResult, origin: str) -> tuple[list[str], str | None]:
142
158
  """The messages this router will repeat, and the first connect URL it will name.
143
159
 
@@ -279,7 +295,16 @@ class Upstream:
279
295
  result = await self._run(call, elicitation_callback=recorder)
280
296
  if isinstance(result, types.InputRequiredResult):
281
297
  lines, url = _relayable_requests(result, self.profile["host"])
282
- return self._connect_refusal(lines, url)
298
+ if _has_url_request(result):
299
+ return self._connect_refusal(lines, url)
300
+ # Every mutating tool gates a `confirmed:false` call behind a form-mode
301
+ # elicitation once the router advertises elicitation at all (see
302
+ # `ConnectUrlRecorder`). That gate is a preview, not a connect flow: the
303
+ # factory's own answer to a client with no elicitation capability is a
304
+ # plain `pending_confirmation` result, and the router owes the caller
305
+ # the closest thing it can relay to that, not an error pointing at a
306
+ # web UI the caller was never asked to visit.
307
+ return self._preview_result(lines)
283
308
  if recorder.declined_url_request:
284
309
  # The older-revision path. The server elicited in band and carried on
285
310
  # once the router declined, so the connect page is in the recorder
@@ -290,6 +315,24 @@ class Upstream:
290
315
  return self._connect_refusal(_result_texts(result), recorder.url)
291
316
  return result
292
317
 
318
+ def _preview_result(self, lines: list[str]) -> types.CallToolResult:
319
+ """A form-mode approval gate, relayed as the ordinary result it is.
320
+
321
+ This loses the structured `pending_confirmation` JSON a client with no
322
+ elicitation capability would see (the `approvalId`, `preview`,
323
+ `impactedResources`): the router has only the elicitation's message
324
+ text to work with, not the tool's own result body, which the factory
325
+ never sends once it has decided to gate the call. The message still
326
+ carries what a caller needs, and the caller still proceeds the same
327
+ way, by calling again with `confirmed:true`.
328
+
329
+ Not an error: the call is still pending, exactly as it would be for a
330
+ client that never advertised elicitation, and marking this `is_error`
331
+ would make a normal preview indistinguishable from the connect refusal
332
+ it is not.
333
+ """
334
+ return types.CallToolResult(content=[types.TextContent(text="\n".join(lines))], is_error=False)
335
+
293
336
  def _connect_refusal(self, lines: list[str], url: str | None) -> types.CallToolResult:
294
337
  """Turn the factory's request for input into a refusal naming the page to visit.
295
338
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: insightfactory-cli
3
- Version: 1.1.2.dev29
3
+ Version: 1.1.2.dev30
4
4
  Summary: Profile-based authentication CLI for the InsightFactory Interfaces API
5
5
  Project-URL: Homepage, https://insightfactory.ai
6
6
  Author-email: "insightfactory.ai Support" <support@insightfactory.ai>
@@ -27,9 +27,9 @@ if_cli/router/content.py,sha256=POL2n5NIY8yenQHJe8pUbTuTNpPSUSm0Y6Txbvehzhs,1448
27
27
  if_cli/router/log.py,sha256=YLWeHUb05HUnS3XHubCwy7oLZAUEGAiHcZysz_YMAAo,412
28
28
  if_cli/router/policy.py,sha256=CclbjuOA5mwwoQiNDa17iQeN9eXRbfy11bS9JFd8gL4,2840
29
29
  if_cli/router/server.py,sha256=JZ-7xbNyDadSY1VVLHpSVTjfx6vxI_uumxFaD22Pl0Y,26449
30
- if_cli/router/upstream.py,sha256=2V78k6SXnOJ9Epc0tnhTuUPVBrYuW5vwflT-d3xKwVc,16122
31
- insightfactory_cli-1.1.2.dev29.dist-info/METADATA,sha256=4M5zoiKKqT52vOoEOrx42CE_TsGyFawx1-4CLa7v5JE,23695
32
- insightfactory_cli-1.1.2.dev29.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
33
- insightfactory_cli-1.1.2.dev29.dist-info/entry_points.txt,sha256=UlX854D7f-7dBj5F0wGdakVy6vopvP9hA1BN-gv739w,44
34
- insightfactory_cli-1.1.2.dev29.dist-info/licenses/LICENSE,sha256=8eZ1YAABL398qESVJc_FlK1voYQ0GAh-R_rNBQK6QH8,234
35
- insightfactory_cli-1.1.2.dev29.dist-info/RECORD,,
30
+ if_cli/router/upstream.py,sha256=Ga0OM918Y1CFyQFGs2wO0ocyjcMe1Wk0LvffOZOaE34,18559
31
+ insightfactory_cli-1.1.2.dev30.dist-info/METADATA,sha256=slUXCeTP0H1dPYnOUVV2AZyWBDHlteeG3Grz5dED0Fc,23695
32
+ insightfactory_cli-1.1.2.dev30.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
33
+ insightfactory_cli-1.1.2.dev30.dist-info/entry_points.txt,sha256=UlX854D7f-7dBj5F0wGdakVy6vopvP9hA1BN-gv739w,44
34
+ insightfactory_cli-1.1.2.dev30.dist-info/licenses/LICENSE,sha256=8eZ1YAABL398qESVJc_FlK1voYQ0GAh-R_rNBQK6QH8,234
35
+ insightfactory_cli-1.1.2.dev30.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.3
2
+ Generator: hatchling 1.32.4
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any