agentgraph-server 0.6.0__tar.gz → 0.6.1__tar.gz
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.
- {agentgraph_server-0.6.0/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/SKILL.md +1 -1
- {agentgraph_server-0.6.0/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/references/data-model.md +3 -1
- {agentgraph_server-0.6.0/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/references/operations.md +25 -4
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/.agents/skills/slack-auth/SKILL.md +4 -2
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/PKG-INFO +16 -15
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/README.md +3 -3
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/backend.py +60 -2
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/schema.sql +2 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/cli.py +60 -36
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/cli_query.py +19 -4
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/connectors/base.py +32 -1
- agentgraph_server-0.6.1/agentgraph/connectors/command_effects.py +69 -0
- agentgraph_server-0.6.1/agentgraph/connectors/feed.py +153 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/connectors/registry.py +8 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/core/storage.py +13 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/demo.py +3 -1
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/bookmark.py +23 -2
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/delete.py +14 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/query.py +5 -4
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/mcp/server.py +40 -8
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/meta_api.py +1 -1
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/observation.py +58 -2
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/static/viewer.html +23 -9
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/skills.py +6 -4
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/PKG-INFO +16 -15
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/SOURCES.txt +8 -4
- agentgraph_server-0.6.1/agentgraph_server.egg-info/requires.txt +38 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/pyproject.toml +15 -14
- agentgraph_server-0.6.1/tests/test_auth_status_methods.py +47 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_browse.py +7 -1
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_cli.py +234 -37
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_command_effects.py +58 -2
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_connectors.py +28 -1
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_docs_build.py +10 -5
- agentgraph_server-0.6.1/tests/test_entity_types.py +100 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_expiration.py +28 -0
- agentgraph_server-0.6.1/tests/test_feed_connectors.py +248 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_observation.py +54 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_query.py +152 -4
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_registry.py +14 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_release_metadata.py +1 -1
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_rss_connector.py +466 -25
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_slack_auth_artifacts.py +2 -1
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_slack_oauth.py +32 -6
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_upsert.py +60 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_viewer_layout_contract.py +38 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_web_connector.py +210 -7
- agentgraph_server-0.6.0/agentgraph/connectors/command_effects.py +0 -18
- agentgraph_server-0.6.0/agentgraph_server.egg-info/requires.txt +0 -37
- {agentgraph_server-0.6.0/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/references/commands.md +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/LICENSE +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/__init__.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/auth/__init__.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/auth/credentials.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/__init__.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/__init__.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/vector.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/cli_sync.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/config.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/connectors/__init__.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/connectors/status.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/core/__init__.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/core/context.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/core/runtime.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/demo_fixtures/reliable-webhooks.md +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/demo_fixtures/retry-guidance.md +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/__init__.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/download.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/embeddings.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/expiration.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/fetch.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/link.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/operations.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/person.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/upsert.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/logging.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/mcp/__init__.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/perf.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/__init__.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/app.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/cli_api.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/router.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/sync.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/sync_api.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/dependency_links.txt +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/entry_points.txt +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/top_level.txt +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/setup.cfg +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_auth.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_benchmarks.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_config.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_discord_attachments.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_embeddings.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_fetch.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_graph_operations.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_launch_demo.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_logging.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_router.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_schema.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_server_imports.py +0 -0
- {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_sync.py +0 -0
|
@@ -11,8 +11,10 @@
|
|
|
11
11
|
| `Message` | Chat messages. Chat images and uploads are stored in `metadata.attachments`. |
|
|
12
12
|
| `Person` | Source identities and confirmed cross-source identity merges. |
|
|
13
13
|
| `Spreadsheet` | Google Sheets and other spreadsheet resources. |
|
|
14
|
+
| `Task` | Tracked work items such as Jira issues. Issue key, status, and assignee live in `metadata`. |
|
|
15
|
+
| `Video` | Recorded videos such as Looms. The transcript is indexed as content and `metadata.web_url` links to the video. |
|
|
14
16
|
|
|
15
|
-
The valid core model does not currently include `
|
|
17
|
+
The valid core model does not currently include `Project`.
|
|
16
18
|
|
|
17
19
|
## Relationships
|
|
18
20
|
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
## Availability and authentication
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
agentgraph connectors [--verify] [--json]
|
|
6
|
+
agentgraph list-connectors [--verify] [--json]
|
|
7
7
|
agentgraph auth [--verify] [--json] status
|
|
8
8
|
agentgraph auth <provider> [--add] [--account <account-id>]
|
|
9
9
|
agentgraph auth remove <provider> [--account <account-id>] [--json]
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
Use `agentgraph connectors --json` to discover installed connector source names,
|
|
12
|
+
Use `agentgraph list-connectors --json` to discover installed connector source names,
|
|
13
13
|
valid platform values, URL ownership, polling delegation, and sync state. Do not rely
|
|
14
14
|
on a hardcoded platform list. Use `--verify` only when a live provider check is
|
|
15
|
-
needed.
|
|
15
|
+
needed. Without `--json`, the CLI renders the same status as an operator-facing table.
|
|
16
16
|
|
|
17
17
|
MCP equivalents are `list_connectors_tool`, `list_auth_providers_tool`,
|
|
18
18
|
`authenticate_provider_tool`, and `remove_auth_provider_tool`.
|
|
@@ -45,6 +45,22 @@ agentgraph connector gmail ingest [--account <account-id>] [--json]
|
|
|
45
45
|
The MCP form is `run_connector_command_tool("gmail", ["ingest", ...])`. There is no
|
|
46
46
|
top-level `agentgraph ingest` command or `ingest_connector_tool`.
|
|
47
47
|
|
|
48
|
+
For an oversized HTML page, request a one-off web fetch that removes style, script,
|
|
49
|
+
noscript, and comment blocks before applying the response-size limit:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
agentgraph connector web fetch <url> --compact [--json]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The MCP form is `run_connector_command_tool("web", ["fetch", "<url>", "--compact"])`.
|
|
56
|
+
Compaction applies only to that command; ordinary fetches, bookmarks, observations, and
|
|
57
|
+
RSS article hydration retain their default behavior.
|
|
58
|
+
|
|
59
|
+
Configure browser observation rules with `agentgraph connector web observe <url-or-prefix>`
|
|
60
|
+
and remove them with `agentgraph connector web observe <url-or-prefix> --remove`. The MCP
|
|
61
|
+
forms are `run_connector_command_tool("web", ["observe", "<url-or-prefix>"])` and
|
|
62
|
+
`run_connector_command_tool("web", ["observe", "<url-or-prefix>", "--remove"])`.
|
|
63
|
+
|
|
48
64
|
## Files and retained context
|
|
49
65
|
|
|
50
66
|
```bash
|
|
@@ -71,7 +87,12 @@ commands, request permission to contact the configured localhost server and retr
|
|
|
71
87
|
```bash
|
|
72
88
|
agentgraph mcp-config
|
|
73
89
|
agentgraph mcp-serve
|
|
74
|
-
agentgraph install-skill [
|
|
90
|
+
agentgraph install-skill [agentgraph] [--target user|project] [--no-claude] [--force] [--json]
|
|
75
91
|
```
|
|
76
92
|
|
|
93
|
+
`agentgraph mcp-config` prints local stdio setup for ChatGPT Desktop Work Mode and
|
|
94
|
+
Claude Desktop. In ChatGPT Desktop, enter the printed executable in **Command to
|
|
95
|
+
launch** and `mcp-serve` in **Arguments**; for Claude Desktop, add the printed JSON
|
|
96
|
+
to its MCP configuration file.
|
|
97
|
+
|
|
77
98
|
The skill installer is available only through the `agentgraph install-skill` CLI command.
|
|
@@ -28,8 +28,10 @@ permission in the target workspace.
|
|
|
28
28
|
Admins create AgentGraph at `https://api.slack.com/apps` with **Create New App →
|
|
29
29
|
From an app manifest**, select the target workspace, paste the manifest printed by
|
|
30
30
|
AgentGraph into Slack's **JSON** tab, approve it, and enter its Client ID. The
|
|
31
|
-
manifest includes the
|
|
32
|
-
|
|
31
|
+
manifest includes the callback, which defaults to `http://localhost:8766/slack/oauth/callback`; set
|
|
32
|
+
`AGENTGRAPH_SLACK_OAUTH_CALLBACK_PORT` before setup to use another local port. The
|
|
33
|
+
manifest printed by AgentGraph uses that port, which must match the Slack app's
|
|
34
|
+
registered redirect URL.
|
|
33
35
|
|
|
34
36
|
Non-admins choose either **Enter a Client ID provided by a Slack admin** or **Set up
|
|
35
37
|
the AgentGraph Slack App**. The setup path checks whether the target workspace
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentgraph-server
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: A local-first CLI and MCP server that turns selected digital sources into a searchable graph for the AI agent you already use.
|
|
5
5
|
Requires-Python: >=3.12
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
7
|
License-File: LICENSE
|
|
8
8
|
Requires-Dist: aiosqlite>=0.20.0
|
|
9
|
-
Requires-Dist: agentgraph-connector-web<0.
|
|
9
|
+
Requires-Dist: agentgraph-connector-web<0.7,>=0.5.4
|
|
10
10
|
Requires-Dist: apscheduler>=3.11.2
|
|
11
11
|
Requires-Dist: click<8.4
|
|
12
12
|
Requires-Dist: fastapi>=0.135.2
|
|
@@ -16,26 +16,27 @@ Requires-Dist: mcp>=1.26.0
|
|
|
16
16
|
Requires-Dist: numpy>=2.4.6
|
|
17
17
|
Requires-Dist: pydantic>=2.12.5
|
|
18
18
|
Requires-Dist: pydantic-settings>=2.13.1
|
|
19
|
+
Requires-Dist: questionary>=2.1.1
|
|
19
20
|
Requires-Dist: rich>=14.3.3
|
|
20
21
|
Requires-Dist: sqlite-vec>=0.1.9
|
|
21
22
|
Requires-Dist: typer>=0.24.1
|
|
22
23
|
Requires-Dist: uvicorn>=0.42.0
|
|
23
24
|
Provides-Extra: google
|
|
24
|
-
Requires-Dist: agentgraph-connector-google<0.
|
|
25
|
+
Requires-Dist: agentgraph-connector-google<0.7,>=0.5.4; extra == "google"
|
|
25
26
|
Provides-Extra: slack
|
|
26
|
-
Requires-Dist: agentgraph-connector-slack<0.
|
|
27
|
+
Requires-Dist: agentgraph-connector-slack<0.7,>=0.5.4; extra == "slack"
|
|
27
28
|
Provides-Extra: discord
|
|
28
|
-
Requires-Dist: agentgraph-connector-discord<0.
|
|
29
|
+
Requires-Dist: agentgraph-connector-discord<0.7,>=0.5.4; extra == "discord"
|
|
29
30
|
Provides-Extra: rss
|
|
30
|
-
Requires-Dist: agentgraph-connector-rss<0.
|
|
31
|
+
Requires-Dist: agentgraph-connector-rss<0.7,>=0.5.4; extra == "rss"
|
|
31
32
|
Provides-Extra: web
|
|
32
|
-
Requires-Dist: agentgraph-connector-web<0.
|
|
33
|
+
Requires-Dist: agentgraph-connector-web<0.7,>=0.5.4; extra == "web"
|
|
33
34
|
Provides-Extra: all
|
|
34
|
-
Requires-Dist: agentgraph-connector-web<0.
|
|
35
|
-
Requires-Dist: agentgraph-connector-google<0.
|
|
36
|
-
Requires-Dist: agentgraph-connector-slack<0.
|
|
37
|
-
Requires-Dist: agentgraph-connector-discord<0.
|
|
38
|
-
Requires-Dist: agentgraph-connector-rss<0.
|
|
35
|
+
Requires-Dist: agentgraph-connector-web<0.7,>=0.5.4; extra == "all"
|
|
36
|
+
Requires-Dist: agentgraph-connector-google<0.7,>=0.5.4; extra == "all"
|
|
37
|
+
Requires-Dist: agentgraph-connector-slack<0.7,>=0.5.4; extra == "all"
|
|
38
|
+
Requires-Dist: agentgraph-connector-discord<0.7,>=0.5.4; extra == "all"
|
|
39
|
+
Requires-Dist: agentgraph-connector-rss<0.7,>=0.5.4; extra == "all"
|
|
39
40
|
Dynamic: license-file
|
|
40
41
|
|
|
41
42
|
# AgentGraph
|
|
@@ -46,7 +47,7 @@ AgentGraph is a local-first, self-hosted CLI and MCP server that turns the sourc
|
|
|
46
47
|
|
|
47
48
|
> **AgentGraph stores context; your agent reasons over it.** It is not an agent, chatbot, hosted graph service, or replacement for the tools you already use.
|
|
48
49
|
|
|
49
|
-
[See the demo](docs-src/demo.md) · [Install](docs-src/install.md) · [Documentation](https://
|
|
50
|
+
[See the demo](docs-src/demo.md) · [Install](docs-src/install.md) · [Documentation](https://agentgraph.simonwa.de/) · [Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi)
|
|
50
51
|
|
|
51
52
|
<picture>
|
|
52
53
|
<source media="(prefers-color-scheme: dark)" srcset="docs-src/assets/diagrams/architecture-overview-dark.svg">
|
|
@@ -110,7 +111,7 @@ agentgraph serve
|
|
|
110
111
|
Install the [AgentGraph Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi), browse a supported resource, then verify that context landed:
|
|
111
112
|
|
|
112
113
|
```bash
|
|
113
|
-
agentgraph connectors
|
|
114
|
+
agentgraph list-connectors
|
|
114
115
|
agentgraph search "project kickoff notes"
|
|
115
116
|
agentgraph traverse <entity-id> --depth 2
|
|
116
117
|
```
|
|
@@ -121,7 +122,7 @@ Connect the agent you already use:
|
|
|
121
122
|
agentgraph mcp-config
|
|
122
123
|
```
|
|
123
124
|
|
|
124
|
-
Use the printed
|
|
125
|
+
Use the printed instructions to connect ChatGPT Desktop Work Mode or Claude Desktop to the local stdio server. See [Install](docs-src/install.md) for the complete setup path.
|
|
125
126
|
|
|
126
127
|
## What AgentGraph is and is not
|
|
127
128
|
|
|
@@ -6,7 +6,7 @@ AgentGraph is a local-first, self-hosted CLI and MCP server that turns the sourc
|
|
|
6
6
|
|
|
7
7
|
> **AgentGraph stores context; your agent reasons over it.** It is not an agent, chatbot, hosted graph service, or replacement for the tools you already use.
|
|
8
8
|
|
|
9
|
-
[See the demo](docs-src/demo.md) · [Install](docs-src/install.md) · [Documentation](https://
|
|
9
|
+
[See the demo](docs-src/demo.md) · [Install](docs-src/install.md) · [Documentation](https://agentgraph.simonwa.de/) · [Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi)
|
|
10
10
|
|
|
11
11
|
<picture>
|
|
12
12
|
<source media="(prefers-color-scheme: dark)" srcset="docs-src/assets/diagrams/architecture-overview-dark.svg">
|
|
@@ -70,7 +70,7 @@ agentgraph serve
|
|
|
70
70
|
Install the [AgentGraph Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi), browse a supported resource, then verify that context landed:
|
|
71
71
|
|
|
72
72
|
```bash
|
|
73
|
-
agentgraph connectors
|
|
73
|
+
agentgraph list-connectors
|
|
74
74
|
agentgraph search "project kickoff notes"
|
|
75
75
|
agentgraph traverse <entity-id> --depth 2
|
|
76
76
|
```
|
|
@@ -81,7 +81,7 @@ Connect the agent you already use:
|
|
|
81
81
|
agentgraph mcp-config
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Use the printed
|
|
84
|
+
Use the printed instructions to connect ChatGPT Desktop Work Mode or Claude Desktop to the local stdio server. See [Install](docs-src/install.md) for the complete setup path.
|
|
85
85
|
|
|
86
86
|
## What AgentGraph is and is not
|
|
87
87
|
|
|
@@ -64,7 +64,7 @@ _LIST_PAGE_ORDER_BY = {
|
|
|
64
64
|
_COLUMN_FILTERS = {"platform", "platform_entity_id", "entity_type"}
|
|
65
65
|
_FTS_DELETE_CHUNK_SIZE = 500
|
|
66
66
|
_BUSY_TIMEOUT_MS = 5_000
|
|
67
|
-
_SCHEMA_VERSION =
|
|
67
|
+
_SCHEMA_VERSION = 4
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def _now() -> str:
|
|
@@ -291,6 +291,10 @@ class SQLiteBackend(StorageBackend):
|
|
|
291
291
|
await conn.execute(
|
|
292
292
|
"CREATE INDEX IF NOT EXISTS idx_entities_platform_type_observed_at ON entities(platform, entity_type, observed_at DESC)"
|
|
293
293
|
)
|
|
294
|
+
await conn.execute(
|
|
295
|
+
"CREATE INDEX IF NOT EXISTS idx_entities_platform_type_feed_updated "
|
|
296
|
+
"ON entities(platform, entity_type, json_extract(metadata, '$.feed_url'), updated_at DESC)"
|
|
297
|
+
)
|
|
294
298
|
|
|
295
299
|
async def _retention_policy_needs_migration(self) -> bool:
|
|
296
300
|
"""Return whether the entity policy constraint lacks persistent support."""
|
|
@@ -348,7 +352,8 @@ class SQLiteBackend(StorageBackend):
|
|
|
348
352
|
observed = "NULL"
|
|
349
353
|
if "observed_at" in columns and "cumulative_observation_duration_ms" in columns:
|
|
350
354
|
observed = (
|
|
351
|
-
"CASE WHEN entity_type IN
|
|
355
|
+
"CASE WHEN entity_type IN "
|
|
356
|
+
"('Channel', 'Document', 'Email', 'Folder', 'Spreadsheet', 'Task', 'Video') "
|
|
352
357
|
"AND cumulative_observation_duration_ms > 0 THEN observed_at ELSE NULL END"
|
|
353
358
|
)
|
|
354
359
|
observed = (
|
|
@@ -1454,6 +1459,59 @@ class SQLiteBackend(StorageBackend):
|
|
|
1454
1459
|
)
|
|
1455
1460
|
return [_row_to_entity(row) for row in rows]
|
|
1456
1461
|
|
|
1462
|
+
async def list_recent_metadata_by_group(
|
|
1463
|
+
self,
|
|
1464
|
+
entity_type: str,
|
|
1465
|
+
filters: dict[str, str],
|
|
1466
|
+
group_metadata_key: str,
|
|
1467
|
+
group_values: list[str],
|
|
1468
|
+
per_group_limit: int,
|
|
1469
|
+
order_by: str,
|
|
1470
|
+
) -> list[dict[str, Any]]:
|
|
1471
|
+
if not group_values or per_group_limit <= 0:
|
|
1472
|
+
return []
|
|
1473
|
+
if order_by not in _VALID_ORDER_BY:
|
|
1474
|
+
order_by = "updated_at"
|
|
1475
|
+
|
|
1476
|
+
params: list[Any] = [entity_type]
|
|
1477
|
+
clauses: list[str] = []
|
|
1478
|
+
for key, value in filters.items():
|
|
1479
|
+
if key in _COLUMN_FILTERS:
|
|
1480
|
+
clauses.append(f"e.{key} = ?")
|
|
1481
|
+
else:
|
|
1482
|
+
clauses.append(f"json_extract(e.metadata, '$.{key}') = ?")
|
|
1483
|
+
params.append(value)
|
|
1484
|
+
|
|
1485
|
+
group_path = f"$.{group_metadata_key}"
|
|
1486
|
+
value_placeholders = ", ".join("?" for _ in group_values)
|
|
1487
|
+
clauses.append(f"json_extract(e.metadata, ?) IN ({value_placeholders})")
|
|
1488
|
+
where_extra = " AND ".join(clauses)
|
|
1489
|
+
|
|
1490
|
+
with timed(
|
|
1491
|
+
"sqlite.list_recent_metadata_by_group",
|
|
1492
|
+
entity_type=entity_type,
|
|
1493
|
+
per_group_limit=per_group_limit,
|
|
1494
|
+
group_count=len(group_values),
|
|
1495
|
+
):
|
|
1496
|
+
rows = await self._fetchall(
|
|
1497
|
+
f"""
|
|
1498
|
+
SELECT metadata FROM (
|
|
1499
|
+
SELECT e.metadata,
|
|
1500
|
+
json_extract(e.metadata, ?) AS group_value,
|
|
1501
|
+
ROW_NUMBER() OVER (
|
|
1502
|
+
PARTITION BY json_extract(e.metadata, ?)
|
|
1503
|
+
ORDER BY e.{order_by} DESC
|
|
1504
|
+
) AS group_rank
|
|
1505
|
+
FROM entities e
|
|
1506
|
+
WHERE e.entity_type = ? AND {where_extra}
|
|
1507
|
+
)
|
|
1508
|
+
WHERE group_rank <= ?
|
|
1509
|
+
ORDER BY group_value, group_rank
|
|
1510
|
+
""",
|
|
1511
|
+
[group_path, group_path, *params, group_path, *group_values, per_group_limit],
|
|
1512
|
+
)
|
|
1513
|
+
return [json.loads(row["metadata"]) if row["metadata"] else {} for row in rows]
|
|
1514
|
+
|
|
1457
1515
|
# --- Read: edges ---
|
|
1458
1516
|
|
|
1459
1517
|
async def get_edges(
|
|
@@ -68,6 +68,8 @@ CREATE INDEX IF NOT EXISTS idx_entities_type_created_at ON entities(entity_type,
|
|
|
68
68
|
CREATE INDEX IF NOT EXISTS idx_entities_type_updated_at ON entities(entity_type, updated_at DESC);
|
|
69
69
|
CREATE INDEX IF NOT EXISTS idx_entities_platform_observed_at_id ON entities(platform, observed_at DESC, id ASC);
|
|
70
70
|
CREATE INDEX IF NOT EXISTS idx_entities_platform_type_observed_at ON entities(platform, entity_type, observed_at DESC);
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_entities_platform_type_feed_updated
|
|
72
|
+
ON entities(platform, entity_type, json_extract(metadata, '$.feed_url'), updated_at DESC);
|
|
71
73
|
|
|
72
74
|
-- Edge indexes
|
|
73
75
|
CREATE INDEX IF NOT EXISTS idx_edges_source ON edges(source_entity_id);
|
|
@@ -12,7 +12,10 @@ from importlib.metadata import PackageNotFoundError
|
|
|
12
12
|
from importlib.metadata import version as package_version
|
|
13
13
|
from typing import TYPE_CHECKING, cast
|
|
14
14
|
|
|
15
|
+
import questionary # type: ignore[import-untyped]
|
|
15
16
|
import typer
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.table import Table
|
|
16
19
|
|
|
17
20
|
if TYPE_CHECKING:
|
|
18
21
|
from agentgraph.connectors.base import BaseConnector
|
|
@@ -22,6 +25,7 @@ app = typer.Typer(
|
|
|
22
25
|
help="Local knowledge graph for AI agents.",
|
|
23
26
|
no_args_is_help=True,
|
|
24
27
|
)
|
|
28
|
+
console = Console()
|
|
25
29
|
|
|
26
30
|
|
|
27
31
|
def _version_callback(value: bool) -> None:
|
|
@@ -81,6 +85,20 @@ def _status_label(status: str) -> str:
|
|
|
81
85
|
)
|
|
82
86
|
|
|
83
87
|
|
|
88
|
+
def _connector_auth_label(item: dict[str, object]) -> str:
|
|
89
|
+
"""Build the authentication cell for a connector status row."""
|
|
90
|
+
status = item["auth_status"]
|
|
91
|
+
if status is None:
|
|
92
|
+
return ""
|
|
93
|
+
|
|
94
|
+
status_label = str(status)
|
|
95
|
+
auth = _status_label(status_label)
|
|
96
|
+
detail = item["auth_detail"]
|
|
97
|
+
if detail:
|
|
98
|
+
auth = f"{auth} ({detail})" if status_label != "ok" else f"{auth} as {detail}"
|
|
99
|
+
return f"{auth} via {item['auth_provider']}"
|
|
100
|
+
|
|
101
|
+
|
|
84
102
|
def _server_is_running() -> bool:
|
|
85
103
|
"""Return whether the configured AgentGraph server responds to health checks."""
|
|
86
104
|
from agentgraph.config import get_settings
|
|
@@ -367,7 +385,7 @@ def connector_command(
|
|
|
367
385
|
help: bool = typer.Option(False, "--help", help="Show this message and exit."),
|
|
368
386
|
) -> None:
|
|
369
387
|
"""Run a connector-owned command."""
|
|
370
|
-
from agentgraph.connectors.registry import bootstrap, get_connector
|
|
388
|
+
from agentgraph.connectors.registry import bootstrap, get_connector, get_connector_load_error
|
|
371
389
|
|
|
372
390
|
if source is None:
|
|
373
391
|
from typer.rich_utils import rich_format_help
|
|
@@ -378,6 +396,10 @@ def connector_command(
|
|
|
378
396
|
bootstrap()
|
|
379
397
|
connector = get_connector(source)
|
|
380
398
|
if connector is None:
|
|
399
|
+
if error := get_connector_load_error(source):
|
|
400
|
+
typer.echo(f"Failed to load connector '{source}': {error}", err=True)
|
|
401
|
+
typer.echo("Reinstall AgentGraph and its connector dependencies, then retry.", err=True)
|
|
402
|
+
raise typer.Exit(code=1)
|
|
381
403
|
typer.echo(f"Unknown connector '{source}'", err=True)
|
|
382
404
|
raise typer.Exit(code=1)
|
|
383
405
|
|
|
@@ -394,6 +416,17 @@ def connector_command(
|
|
|
394
416
|
from agentgraph.connectors.command_effects import execute_deletions
|
|
395
417
|
|
|
396
418
|
result["deleted_entities"] = run_graph_operation(lambda: execute_deletions(effects))
|
|
419
|
+
if effects.fetch_references:
|
|
420
|
+
from agentgraph.cli_query import run_graph_operation
|
|
421
|
+
from agentgraph.connectors.command_effects import (
|
|
422
|
+
execute_fetches,
|
|
423
|
+
fetch_effect_error_hint,
|
|
424
|
+
)
|
|
425
|
+
|
|
426
|
+
result["fetched"] = run_graph_operation(
|
|
427
|
+
lambda: execute_fetches(effects),
|
|
428
|
+
error_hint=lambda error: fetch_effect_error_hint(effects, error, "cli"),
|
|
429
|
+
)
|
|
397
430
|
if effects.poll:
|
|
398
431
|
from agentgraph.cli_sync import queue_connector_poll
|
|
399
432
|
|
|
@@ -418,8 +451,8 @@ def connector_command(
|
|
|
418
451
|
typer.echo(type(connector).format_cli_result(result))
|
|
419
452
|
|
|
420
453
|
|
|
421
|
-
@app.command()
|
|
422
|
-
def
|
|
454
|
+
@app.command(name="list-connectors")
|
|
455
|
+
def list_connectors(
|
|
423
456
|
json: bool = typer.Option(False, "--json", help="Output as JSON"),
|
|
424
457
|
verify: bool = typer.Option(
|
|
425
458
|
False, "--verify", help="Live-check connector credentials with provider APIs"
|
|
@@ -443,23 +476,22 @@ def connectors(
|
|
|
443
476
|
typer.echo(_json.dumps(items, indent=2))
|
|
444
477
|
return
|
|
445
478
|
|
|
479
|
+
table = Table(title="Connectors", show_lines=True)
|
|
480
|
+
table.add_column("Connector", style="bold", no_wrap=True)
|
|
481
|
+
table.add_column("Description", ratio=1)
|
|
482
|
+
table.add_column("Auth")
|
|
483
|
+
table.add_column("Sync")
|
|
484
|
+
table.add_column("Last sync", no_wrap=True)
|
|
446
485
|
for item in items:
|
|
447
|
-
sync = str(item["sync"])
|
|
448
|
-
last_sync = str(item["last_sync"])
|
|
449
486
|
desc = item["description"] or item["source"]
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
detail = item["auth_detail"]
|
|
457
|
-
auth = _status_label(status_label)
|
|
458
|
-
if detail:
|
|
459
|
-
auth = f"{auth} ({detail})" if status_label != "ok" else f"{auth} as {detail}"
|
|
460
|
-
typer.echo(
|
|
461
|
-
f" {'':<12} auth: {auth} via {item['auth_provider']} | sync: {sync} | last sync: {last_sync}"
|
|
487
|
+
table.add_row(
|
|
488
|
+
str(item["source"]),
|
|
489
|
+
str(desc),
|
|
490
|
+
_connector_auth_label(item),
|
|
491
|
+
str(item["sync"]),
|
|
492
|
+
str(item["last_sync"]),
|
|
462
493
|
)
|
|
494
|
+
console.print(table)
|
|
463
495
|
|
|
464
496
|
|
|
465
497
|
@app.command()
|
|
@@ -657,7 +689,7 @@ def onboard() -> None:
|
|
|
657
689
|
prompt: str = getattr(connector, "onboard_prompt", None) or f"Set up {label}?"
|
|
658
690
|
description: str = getattr(connector, "auth_description", None) or label.title()
|
|
659
691
|
typer.echo(f"Step {i}/{total}: {description}")
|
|
660
|
-
if
|
|
692
|
+
if questionary.confirm(f" {prompt}", default=True).ask():
|
|
661
693
|
type(connector).run_auth_flow()
|
|
662
694
|
else:
|
|
663
695
|
typer.echo(" Skipped.")
|
|
@@ -689,30 +721,22 @@ def mcp_config() -> None:
|
|
|
689
721
|
}
|
|
690
722
|
}
|
|
691
723
|
|
|
692
|
-
typer.echo("\
|
|
693
|
-
typer.echo("
|
|
694
|
-
typer.echo("
|
|
695
|
-
typer.echo(
|
|
696
|
-
typer.echo()
|
|
697
|
-
typer.echo("
|
|
698
|
-
typer.echo("Run a streamable HTTP MCP server:")
|
|
699
|
-
typer.echo(f" {binary} mcp-serve --transport streamable-http --port 8808")
|
|
700
|
-
typer.echo("Local endpoint:")
|
|
701
|
-
typer.echo(" http://127.0.0.1:8808/mcp")
|
|
702
|
-
typer.echo("ChatGPT requires a reachable HTTPS URL. Use Secure MCP Tunnel, ngrok,")
|
|
703
|
-
typer.echo("or Cloudflare Tunnel, then create an app/connector in ChatGPT Developer mode")
|
|
704
|
-
typer.echo("with the public URL ending in /mcp, for example:")
|
|
705
|
-
typer.echo(" https://your-tunnel.example/mcp")
|
|
724
|
+
typer.echo("\nChatGPT Desktop Work Mode:")
|
|
725
|
+
typer.echo(" Add a local MCP server in the MCP configuration screen.")
|
|
726
|
+
typer.echo(" Command to launch:")
|
|
727
|
+
typer.echo(f" {binary}")
|
|
728
|
+
typer.echo(" Arguments:")
|
|
729
|
+
typer.echo(" mcp-serve")
|
|
706
730
|
typer.echo()
|
|
707
|
-
typer.echo("
|
|
708
|
-
typer.echo(
|
|
709
|
-
typer.echo(
|
|
731
|
+
typer.echo("Claude Desktop:")
|
|
732
|
+
typer.echo(" Add this to ~/Library/Application Support/Claude/claude_desktop_config.json:\n")
|
|
733
|
+
typer.echo(json.dumps(config, indent=2))
|
|
710
734
|
typer.echo()
|
|
711
735
|
|
|
712
736
|
|
|
713
737
|
@app.command()
|
|
714
738
|
def install_skill(
|
|
715
|
-
skill: str = typer.Argument("
|
|
739
|
+
skill: str = typer.Argument("agentgraph", help="Bundled skill to install"),
|
|
716
740
|
target: str = typer.Option(
|
|
717
741
|
"user",
|
|
718
742
|
"--target",
|
|
@@ -24,19 +24,28 @@ async def _with_backend[T](operation: Callable[[], Awaitable[T]]) -> T:
|
|
|
24
24
|
return await operation()
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
def _run[T](
|
|
27
|
+
def _run[T](
|
|
28
|
+
operation: Callable[[], Awaitable[T]],
|
|
29
|
+
error_hint: Callable[[Exception], str | None] | None = None,
|
|
30
|
+
) -> T:
|
|
28
31
|
"""Run one graph operation with a configured backend and concise CLI errors."""
|
|
29
32
|
try:
|
|
30
33
|
return asyncio.run(_with_backend(operation))
|
|
31
34
|
except Exception as exc:
|
|
32
35
|
console.print("[red]AgentGraph command failed:[/red] ", end="")
|
|
33
36
|
console.print(str(exc), markup=False, highlight=False)
|
|
37
|
+
hint = error_hint(exc) if error_hint is not None else None
|
|
38
|
+
if hint:
|
|
39
|
+
console.print(hint, markup=False, highlight=False)
|
|
34
40
|
raise SystemExit(1) from exc
|
|
35
41
|
|
|
36
42
|
|
|
37
|
-
def run_graph_operation[T](
|
|
43
|
+
def run_graph_operation[T](
|
|
44
|
+
operation: Callable[[], Awaitable[T]],
|
|
45
|
+
error_hint: Callable[[Exception], str | None] | None = None,
|
|
46
|
+
) -> T:
|
|
38
47
|
"""Run a graph operation from another CLI command."""
|
|
39
|
-
return _run(operation)
|
|
48
|
+
return _run(operation, error_hint=error_hint)
|
|
40
49
|
|
|
41
50
|
|
|
42
51
|
def cmd_search(
|
|
@@ -224,7 +233,13 @@ def cmd_fetch(platform: str, resource_id: str, as_json: bool) -> None:
|
|
|
224
233
|
|
|
225
234
|
return await fetch_entity(platform, resource_id)
|
|
226
235
|
|
|
227
|
-
|
|
236
|
+
def error_hint(error: Exception) -> str | None:
|
|
237
|
+
from agentgraph.connectors.registry import get_connector
|
|
238
|
+
|
|
239
|
+
connector = get_connector(platform)
|
|
240
|
+
return connector.fetch_error_hint(resource_id, error, "cli") if connector is not None else None
|
|
241
|
+
|
|
242
|
+
result = _run(operation, error_hint=error_hint)
|
|
228
243
|
if as_json:
|
|
229
244
|
console.print_json(json.dumps(result, default=str))
|
|
230
245
|
return
|
|
@@ -15,7 +15,17 @@ from pydantic import BaseModel, Field
|
|
|
15
15
|
|
|
16
16
|
# All resource_type values understood by the connector layer.
|
|
17
17
|
# Each value maps to a distinct fetch strategy within a connector.
|
|
18
|
-
ResourceType = Literal[
|
|
18
|
+
ResourceType = Literal[
|
|
19
|
+
"channel",
|
|
20
|
+
"dm",
|
|
21
|
+
"document",
|
|
22
|
+
"folder",
|
|
23
|
+
"message",
|
|
24
|
+
"spreadsheet",
|
|
25
|
+
"thread",
|
|
26
|
+
"video",
|
|
27
|
+
"work-item",
|
|
28
|
+
]
|
|
19
29
|
RetentionPolicy = Literal["observed", "owned", "connected", "persistent"]
|
|
20
30
|
|
|
21
31
|
# All valid entity_type values stored in the DB.
|
|
@@ -27,6 +37,8 @@ ENTITY_TYPES: tuple[str, ...] = (
|
|
|
27
37
|
"Person",
|
|
28
38
|
"Spreadsheet",
|
|
29
39
|
"Email",
|
|
40
|
+
"Task",
|
|
41
|
+
"Video",
|
|
30
42
|
)
|
|
31
43
|
|
|
32
44
|
# Broad URL extractor — classify_url does fine-grained matching
|
|
@@ -41,6 +53,8 @@ RESOURCE_TYPE_TO_ENTITY_TYPE: dict[str, str] = {
|
|
|
41
53
|
"message": "Message",
|
|
42
54
|
"spreadsheet": "Spreadsheet",
|
|
43
55
|
"thread": "Email",
|
|
56
|
+
"video": "Video",
|
|
57
|
+
"work-item": "Task",
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
|
|
@@ -68,6 +82,7 @@ class ConnectorCommandEffects:
|
|
|
68
82
|
ingest: bool = False
|
|
69
83
|
ingest_account_id: str | None = None
|
|
70
84
|
delete_entities: tuple[EntityReference, ...] = ()
|
|
85
|
+
fetch_references: tuple[SourceReference, ...] = ()
|
|
71
86
|
|
|
72
87
|
|
|
73
88
|
class PersonRecord(BaseModel):
|
|
@@ -225,6 +240,10 @@ class FetchPolicy:
|
|
|
225
240
|
return self.FRESH
|
|
226
241
|
|
|
227
242
|
|
|
243
|
+
class ResourceUnavailableError(RuntimeError):
|
|
244
|
+
"""A connector cannot retrieve a resource because it is unavailable to the account."""
|
|
245
|
+
|
|
246
|
+
|
|
228
247
|
class BaseConnector(ABC):
|
|
229
248
|
source: ClassVar[str] # platform name, e.g. "slack" — must be set by subclass
|
|
230
249
|
fetch_policy: ClassVar[FetchPolicy] # staleness policy — must be set by subclass
|
|
@@ -360,6 +379,8 @@ class BaseConnector(ABC):
|
|
|
360
379
|
"Channel": "channel",
|
|
361
380
|
"Message": "message",
|
|
362
381
|
"Email": "thread",
|
|
382
|
+
"Task": "work-item",
|
|
383
|
+
"Video": "video",
|
|
363
384
|
}
|
|
364
385
|
return resource_id, resource_type_map.get(entity_type, "document") # type: ignore[return-value]
|
|
365
386
|
|
|
@@ -425,6 +446,16 @@ class BaseConnector(ABC):
|
|
|
425
446
|
"""
|
|
426
447
|
...
|
|
427
448
|
|
|
449
|
+
def fetch_error_hint(
|
|
450
|
+
self,
|
|
451
|
+
resource_id: str,
|
|
452
|
+
error: Exception,
|
|
453
|
+
audience: Literal["cli", "mcp"],
|
|
454
|
+
) -> str | None:
|
|
455
|
+
"""Return connector-owned recovery guidance for a failed fetch, if available."""
|
|
456
|
+
_ = (resource_id, error, audience)
|
|
457
|
+
return None
|
|
458
|
+
|
|
428
459
|
async def ingest(self, account_id: str | None = None) -> EntityBatch:
|
|
429
460
|
"""Run a one-shot bulk ingest of all available historical data for this connector.
|
|
430
461
|
|