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.
Files changed (101) hide show
  1. {agentgraph_server-0.6.0/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/SKILL.md +1 -1
  2. {agentgraph_server-0.6.0/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/references/data-model.md +3 -1
  3. {agentgraph_server-0.6.0/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/references/operations.md +25 -4
  4. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/.agents/skills/slack-auth/SKILL.md +4 -2
  5. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/PKG-INFO +16 -15
  6. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/README.md +3 -3
  7. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/backend.py +60 -2
  8. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/schema.sql +2 -0
  9. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/cli.py +60 -36
  10. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/cli_query.py +19 -4
  11. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/connectors/base.py +32 -1
  12. agentgraph_server-0.6.1/agentgraph/connectors/command_effects.py +69 -0
  13. agentgraph_server-0.6.1/agentgraph/connectors/feed.py +153 -0
  14. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/connectors/registry.py +8 -0
  15. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/core/storage.py +13 -0
  16. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/demo.py +3 -1
  17. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/bookmark.py +23 -2
  18. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/delete.py +14 -0
  19. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/query.py +5 -4
  20. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/mcp/server.py +40 -8
  21. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/meta_api.py +1 -1
  22. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/observation.py +58 -2
  23. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/static/viewer.html +23 -9
  24. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/skills.py +6 -4
  25. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/PKG-INFO +16 -15
  26. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/SOURCES.txt +8 -4
  27. agentgraph_server-0.6.1/agentgraph_server.egg-info/requires.txt +38 -0
  28. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/pyproject.toml +15 -14
  29. agentgraph_server-0.6.1/tests/test_auth_status_methods.py +47 -0
  30. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_browse.py +7 -1
  31. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_cli.py +234 -37
  32. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_command_effects.py +58 -2
  33. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_connectors.py +28 -1
  34. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_docs_build.py +10 -5
  35. agentgraph_server-0.6.1/tests/test_entity_types.py +100 -0
  36. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_expiration.py +28 -0
  37. agentgraph_server-0.6.1/tests/test_feed_connectors.py +248 -0
  38. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_observation.py +54 -0
  39. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_query.py +152 -4
  40. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_registry.py +14 -0
  41. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_release_metadata.py +1 -1
  42. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_rss_connector.py +466 -25
  43. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_slack_auth_artifacts.py +2 -1
  44. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_slack_oauth.py +32 -6
  45. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_upsert.py +60 -0
  46. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_viewer_layout_contract.py +38 -0
  47. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_web_connector.py +210 -7
  48. agentgraph_server-0.6.0/agentgraph/connectors/command_effects.py +0 -18
  49. agentgraph_server-0.6.0/agentgraph_server.egg-info/requires.txt +0 -37
  50. {agentgraph_server-0.6.0/.agents/skills/AgentGraph → agentgraph_server-0.6.1/.agents/skills/agentgraph}/references/commands.md +0 -0
  51. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/LICENSE +0 -0
  52. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/__init__.py +0 -0
  53. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/auth/__init__.py +0 -0
  54. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/auth/credentials.py +0 -0
  55. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/__init__.py +0 -0
  56. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/__init__.py +0 -0
  57. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/backends/sqlite/vector.py +0 -0
  58. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/cli_sync.py +0 -0
  59. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/config.py +0 -0
  60. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/connectors/__init__.py +0 -0
  61. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/connectors/status.py +0 -0
  62. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/core/__init__.py +0 -0
  63. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/core/context.py +0 -0
  64. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/core/runtime.py +0 -0
  65. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/demo_fixtures/reliable-webhooks.md +0 -0
  66. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/demo_fixtures/retry-guidance.md +0 -0
  67. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/__init__.py +0 -0
  68. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/download.py +0 -0
  69. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/embeddings.py +0 -0
  70. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/expiration.py +0 -0
  71. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/fetch.py +0 -0
  72. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/link.py +0 -0
  73. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/operations.py +0 -0
  74. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/person.py +0 -0
  75. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/graph/upsert.py +0 -0
  76. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/logging.py +0 -0
  77. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/mcp/__init__.py +0 -0
  78. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/perf.py +0 -0
  79. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/__init__.py +0 -0
  80. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/app.py +0 -0
  81. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/cli_api.py +0 -0
  82. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/router.py +0 -0
  83. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/sync.py +0 -0
  84. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph/server/sync_api.py +0 -0
  85. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/dependency_links.txt +0 -0
  86. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/entry_points.txt +0 -0
  87. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/agentgraph_server.egg-info/top_level.txt +0 -0
  88. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/setup.cfg +0 -0
  89. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_auth.py +0 -0
  90. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_benchmarks.py +0 -0
  91. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_config.py +0 -0
  92. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_discord_attachments.py +0 -0
  93. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_embeddings.py +0 -0
  94. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_fetch.py +0 -0
  95. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_graph_operations.py +0 -0
  96. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_launch_demo.py +0 -0
  97. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_logging.py +0 -0
  98. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_router.py +0 -0
  99. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_schema.py +0 -0
  100. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_server_imports.py +0 -0
  101. {agentgraph_server-0.6.0 → agentgraph_server-0.6.1}/tests/test_sync.py +0 -0
@@ -1,5 +1,5 @@
1
1
  ---
2
- name: AgentGraph
2
+ name: agentgraph
3
3
  description: Use AgentGraph through its CLI or MCP tools to search and traverse selected local context, retrieve full source-backed evidence, fetch missing or stale resources, and troubleshoot connector availability.
4
4
  ---
5
5
 
@@ -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 `Task` or `Project`.
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 [AgentGraph] [--target user|project] [--no-claude] [--force] [--json]
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 only
32
- supported callback, `http://localhost:8766/slack/oauth/callback`.
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.0
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.6,>=0.5.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.6,>=0.5.0; extra == "google"
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.6,>=0.5.0; extra == "slack"
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.6,>=0.5.0; extra == "discord"
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.6,>=0.5.0; extra == "rss"
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.6,>=0.5.0; extra == "web"
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.6,>=0.5.0; extra == "all"
35
- Requires-Dist: agentgraph-connector-google<0.6,>=0.5.0; extra == "all"
36
- Requires-Dist: agentgraph-connector-slack<0.6,>=0.5.0; extra == "all"
37
- Requires-Dist: agentgraph-connector-discord<0.6,>=0.5.0; extra == "all"
38
- Requires-Dist: agentgraph-connector-rss<0.6,>=0.5.0; extra == "all"
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://simonexmachina.github.io/agent-graph/) · [Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi)
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 stdio configuration with Claude Desktop, Claude Code, Codex, or another compatible MCP client. ChatGPT developer mode uses the streamable HTTP `/mcp` endpoint through HTTPS. See [Install](docs-src/install.md) for the complete setup path.
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://simonexmachina.github.io/agent-graph/) · [Chrome extension](https://chromewebstore.google.com/detail/agentgraph-extension/iilkfclglabllelhjacijldknapbhidi)
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 stdio configuration with Claude Desktop, Claude Code, Codex, or another compatible MCP client. ChatGPT developer mode uses the streamable HTTP `/mcp` endpoint through HTTPS. See [Install](docs-src/install.md) for the complete setup path.
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 = 3
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 ('Channel', 'Document', 'Email', 'Folder', 'Spreadsheet') "
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 connectors(
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
- typer.echo(f" {item['source']:<12} {desc}")
451
- status = item["auth_status"]
452
- if status is None:
453
- typer.echo(f" {'':<12} sync: {sync} | last sync: {last_sync}")
454
- continue
455
- status_label = str(status)
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 typer.confirm(f" {prompt}", default=True):
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("\nFor stdio MCP clients, add this to your MCP client config:\n")
693
- typer.echo(" Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json")
694
- typer.echo(" Claude Code: ~/.claude/mcp.json (or .claude/mcp.json in your project)\n")
695
- typer.echo(json.dumps(config, indent=2))
696
- typer.echo()
697
- typer.echo("For ChatGPT developer mode, do not use the stdio JSON above.")
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("SSE is also supported by the server if your MCP client needs it:")
708
- typer.echo(f" {binary} mcp-serve --transport sse --port 8808")
709
- typer.echo(" http://127.0.0.1:8808/sse")
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("AgentGraph", help="Bundled skill to install"),
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](operation: Callable[[], Awaitable[T]]) -> 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](operation: Callable[[], Awaitable[T]]) -> 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
- result = _run(operation)
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["channel", "dm", "document", "folder", "message", "spreadsheet", "thread"]
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