@vellumai/assistant 0.4.43 → 0.4.44
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.
- package/ARCHITECTURE.md +13 -14
- package/README.md +11 -12
- package/docs/architecture/integrations.md +75 -93
- package/package.json +1 -1
- package/src/__tests__/approval-routes-http.test.ts +0 -2
- package/src/__tests__/bundled-asset.test.ts +1 -1
- package/src/__tests__/checker.test.ts +31 -28
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +2 -1
- package/src/__tests__/error-handler-friendly-messages.test.ts +46 -0
- package/src/__tests__/managed-twitter-guardrails.test.ts +5 -1
- package/src/__tests__/onboarding-template-contract.test.ts +0 -10
- package/src/__tests__/provider-fail-open-selection.test.ts +12 -2
- package/src/__tests__/send-endpoint-busy.test.ts +0 -3
- package/src/__tests__/session-confirmation-signals.test.ts +7 -45
- package/src/__tests__/starter-task-flow.test.ts +9 -19
- package/src/__tests__/system-prompt.test.ts +3 -4
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/twitter-platform-proxy-client.test.ts +43 -18
- package/src/cli/commands/amazon/index.ts +4 -39
- package/src/cli/commands/amazon/session.ts +18 -26
- package/src/cli/commands/twitter/__tests__/cli-read-routing.test.ts +58 -196
- package/src/cli/commands/twitter/__tests__/cli-routing.test.ts +26 -186
- package/src/cli/commands/twitter/__tests__/oauth-client.test.ts +1 -47
- package/src/cli/commands/twitter/index.ts +95 -835
- package/src/cli/commands/twitter/oauth-client.ts +1 -35
- package/src/cli/commands/twitter/router.ts +70 -115
- package/src/cli/commands/twitter/types.ts +30 -0
- package/src/cli/reference.ts +2 -2
- package/src/config/bundled-skills/amazon/SKILL.md +0 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +0 -6
- package/src/config/bundled-skills/app-builder/TOOLS.json +0 -4
- package/src/config/bundled-skills/doordash/SKILL.md +0 -1
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +1 -82
- package/src/config/bundled-skills/doordash/doordash-cli.ts +17 -28
- package/src/config/bundled-skills/doordash/lib/session.ts +21 -17
- package/src/config/bundled-skills/twitter/SKILL.md +53 -166
- package/src/config/feature-flag-registry.json +8 -0
- package/src/daemon/handlers/session-history.ts +41 -9
- package/src/daemon/lifecycle.ts +4 -17
- package/src/daemon/message-types/apps.ts +0 -25
- package/src/daemon/message-types/integrations.ts +1 -7
- package/src/daemon/message-types/sessions.ts +6 -1
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/ride-shotgun-handler.ts +33 -1
- package/src/daemon/seed-files.ts +3 -27
- package/src/daemon/server.ts +2 -18
- package/src/daemon/session-agent-loop-handlers.ts +24 -2
- package/src/daemon/session-runtime-assembly.ts +0 -7
- package/src/daemon/session-surfaces.ts +185 -33
- package/src/daemon/session.ts +2 -28
- package/src/memory/app-store.ts +0 -18
- package/src/memory/schema/infrastructure.ts +0 -8
- package/src/permissions/defaults.ts +3 -3
- package/src/prompts/system-prompt.ts +4 -5
- package/src/prompts/templates/BOOTSTRAP.md +0 -3
- package/src/providers/registry.ts +2 -4
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +2 -1
- package/src/runtime/auth/route-policy.ts +0 -4
- package/src/runtime/auth/scopes.ts +1 -0
- package/src/runtime/auth/token-service.ts +1 -1
- package/src/runtime/http-types.ts +10 -0
- package/src/runtime/middleware/error-handler.ts +14 -1
- package/src/runtime/routes/app-management-routes.ts +61 -64
- package/src/runtime/routes/brain-graph/brain-graph.html +1845 -0
- package/src/runtime/routes/brain-graph-routes.ts +4 -42
- package/src/runtime/routes/conversation-routes.ts +9 -6
- package/src/runtime/routes/diagnostics-routes.ts +91 -14
- package/src/runtime/routes/settings-routes.ts +3 -93
- package/src/tools/AGENTS.md +38 -0
- package/src/tools/apps/executors.ts +0 -6
- package/src/tools/document/editor-template.ts +10 -8
- package/src/twitter/platform-proxy-client.ts +6 -3
- package/src/util/errors.ts +12 -0
- package/src/__tests__/home-base-bootstrap.test.ts +0 -84
- package/src/__tests__/prebuilt-home-base-seed.test.ts +0 -79
- package/src/cli/commands/twitter/__tests__/cli-error-shaping.test.ts +0 -265
- package/src/cli/commands/twitter/client.ts +0 -989
- package/src/cli/commands/twitter/session.ts +0 -121
- package/src/home-base/app-link-store.ts +0 -78
- package/src/home-base/bootstrap.ts +0 -74
- package/src/home-base/prebuilt/brain-graph.html +0 -1483
- package/src/home-base/prebuilt/index.html +0 -702
- package/src/home-base/prebuilt/seed-metadata.json +0 -21
- package/src/home-base/prebuilt/seed.ts +0 -122
- package/src/home-base/prebuilt-home-base-updater.ts +0 -36
- package/src/util/cookie-session.ts +0 -98
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* DoorDash session persistence.
|
|
3
|
-
* Stores/loads auth cookies
|
|
3
|
+
* Stores/loads auth cookies via the credential store.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
6
7
|
import {
|
|
7
8
|
existsSync,
|
|
8
9
|
mkdirSync,
|
|
@@ -11,12 +12,12 @@ import {
|
|
|
11
12
|
writeFileSync,
|
|
12
13
|
} from "node:fs";
|
|
13
14
|
import { join } from "node:path";
|
|
15
|
+
import { promisify } from "node:util";
|
|
14
16
|
|
|
15
17
|
import { ConfigError } from "./shared/errors.js";
|
|
16
|
-
import type {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from "./shared/recording-types.js";
|
|
18
|
+
import type { ExtractedCredential } from "./shared/recording-types.js";
|
|
19
|
+
|
|
20
|
+
const execFileAsync = promisify(execFile);
|
|
20
21
|
|
|
21
22
|
export interface DoorDashSession {
|
|
22
23
|
cookies: ExtractedCredential[];
|
|
@@ -56,22 +57,25 @@ export function clearSession(): void {
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
|
-
* Import cookies
|
|
60
|
+
* Import cookies that the daemon saved to the credential store under the
|
|
61
|
+
* target domain key. Copies them into the local DoorDash session file.
|
|
60
62
|
*/
|
|
61
|
-
export function
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
63
|
+
export async function importFromCredentialStore(
|
|
64
|
+
targetDomain: string,
|
|
65
|
+
): Promise<DoorDashSession> {
|
|
66
|
+
const { stdout } = await execFileAsync("assistant", [
|
|
67
|
+
"credentials",
|
|
68
|
+
"reveal",
|
|
69
|
+
`${targetDomain}:session:cookies`,
|
|
70
|
+
]);
|
|
71
|
+
const cookies = JSON.parse(stdout.trim()) as ExtractedCredential[];
|
|
72
|
+
if (!cookies.length) {
|
|
73
|
+
throw new ConfigError("No cookies found in credential store");
|
|
70
74
|
}
|
|
75
|
+
|
|
71
76
|
const session: DoorDashSession = {
|
|
72
|
-
cookies
|
|
77
|
+
cookies,
|
|
73
78
|
importedAt: new Date().toISOString(),
|
|
74
|
-
recordingId: recording.id,
|
|
75
79
|
};
|
|
76
80
|
saveSession(session);
|
|
77
81
|
return session;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: twitter
|
|
3
|
-
description: Read and post on X (formerly Twitter) via OAuth or
|
|
3
|
+
description: Read and post on X (formerly Twitter) via OAuth or managed mode
|
|
4
4
|
compatibility: "Designed for Vellum personal assistants"
|
|
5
5
|
metadata: {"emoji":"𝕏","vellum":{"display-name":"X","user-invocable":true}}
|
|
6
6
|
---
|
|
@@ -9,89 +9,63 @@ You are an X (formerly Twitter) assistant. Use the `bash` tool to run `assistant
|
|
|
9
9
|
|
|
10
10
|
## Connection Options
|
|
11
11
|
|
|
12
|
-
There are
|
|
12
|
+
There are two supported ways to connect to X. Choose whichever fits the user's situation.
|
|
13
13
|
|
|
14
14
|
### Managed mode (platform-hosted credentials)
|
|
15
15
|
|
|
16
|
-
When `twitter.integrationMode` is set to `managed`, the platform holds the OAuth credentials and proxies Twitter API calls on behalf of the assistant. No local OAuth setup
|
|
16
|
+
When `twitter.integrationMode` is set to `managed`, the platform holds the OAuth credentials and proxies Twitter API calls on behalf of the assistant. No local OAuth setup is needed.
|
|
17
17
|
|
|
18
|
-
- Supports: **post** and **
|
|
19
|
-
- Read-only operations still use the browser path when available.
|
|
18
|
+
- Supports: **post**, **reply**, and **read** operations (routed through the platform proxy)
|
|
20
19
|
- Prerequisites: The assistant must be registered with the platform (`PLATFORM_ASSISTANT_ID`), have an API key (`credential:vellum:assistant_api_key`), and the assistant owner must have connected their Twitter account on the platform.
|
|
21
|
-
- The strategy is automatically set to `managed` when `integrationMode` is `managed` and prerequisites are satisfied.
|
|
22
20
|
|
|
23
21
|
**Error scenarios in managed mode:**
|
|
24
|
-
- *"Assistant not bootstrapped"*
|
|
25
|
-
- *"Local assistant not registered with platform"*
|
|
26
|
-
- *"Connect Twitter in Settings as the assistant owner"*
|
|
27
|
-
- *"Sign in as the assistant owner"*
|
|
28
|
-
- *"Reconnect Twitter or retry"*
|
|
22
|
+
- *"Assistant not bootstrapped"* -- The assistant API key is missing. Run setup.
|
|
23
|
+
- *"Local assistant not registered with platform"* -- `PLATFORM_ASSISTANT_ID` is not set.
|
|
24
|
+
- *"Connect Twitter in Settings as the assistant owner"* -- The owner hasn't connected their X account on the platform yet.
|
|
25
|
+
- *"Sign in as the assistant owner"* -- The current user is not the assistant owner.
|
|
26
|
+
- *"Reconnect Twitter or retry"* -- The platform's OAuth token may have expired. Reconnect on the platform.
|
|
29
27
|
|
|
30
28
|
**Architecture notes for managed mode:**
|
|
31
29
|
|
|
32
30
|
- **Assistant hosting mode and Twitter credential mode are separate concepts.** An assistant can be self-hosted (local daemon) yet use managed Twitter credentials, or platform-hosted yet use local BYO OAuth. The `twitter.integrationMode` config controls credential mode; the assistant's hosting mode is determined by its lockfile entry.
|
|
33
31
|
- **Managed Twitter is bound to the assistant owner.** Only the owner of the assistant (as determined by the platform) can connect or disconnect the Twitter account. Non-owner users receive a `403` with an `owner_only` or `owner_credential_required` error code.
|
|
34
|
-
- **Connect/disconnect/status uses desktop
|
|
35
|
-
- **Actual Twitter API calls use assistant-level API key authentication.** At runtime, the proxy client sends `Authorization: Api-Key {assistant_api_key}`
|
|
32
|
+
- **Connect/disconnect/status uses desktop authentication.** The macOS Settings UI calls the platform's Twitter OAuth endpoints using the user's WorkOS-issued token. This authenticates the human user, not the assistant.
|
|
33
|
+
- **Actual Twitter API calls use assistant-level API key authentication.** At runtime, the proxy client sends `Authorization: Api-Key {assistant_api_key}` -- it never includes user-level tokens. This ensures the assistant's identity is what the platform uses for token lookup and rate limiting.
|
|
36
34
|
- **The platform proxy handles token storage and refresh.** OAuth tokens are stored server-side by the platform. The assistant never sees or stores the Twitter OAuth access/refresh tokens in managed mode. Token refresh is handled transparently by the proxy.
|
|
37
35
|
- **The daemon auth handler never starts local OAuth in managed mode.** When `integrationMode` is `managed`, `handleTwitterAuthStart` returns a managed-specific error code (`managed_auth_via_platform` or `managed_missing_api_key`) and never calls `orchestrateOAuthConnect`. This is a critical guardrail to prevent credential confusion.
|
|
38
36
|
|
|
39
37
|
### OAuth (recommended with X developer credentials)
|
|
40
38
|
|
|
41
|
-
OAuth uses the official X API v2. It is the most reliable connection method
|
|
39
|
+
OAuth uses the official X API v2. It is the most reliable local connection method.
|
|
42
40
|
|
|
43
41
|
- Supports: **post** and **reply**
|
|
44
|
-
- Read-only operations (timeline, search, home, bookmarks, notifications, likes, followers, following, media) always use the browser path directly, regardless of the strategy setting.
|
|
45
42
|
- Setup: Collect the OAuth Client ID (and optional Client Secret) from the user in chat using `credential_store` with `action: "prompt"` (canonical field names: `client_id`, `client_secret`), then initiate the `twitter_auth_start` flow. See the **First-Use Decision Flow** for the full sequence.
|
|
46
|
-
- Set the strategy: `assistant config set twitter.operationStrategy oauth`
|
|
47
|
-
|
|
48
|
-
### Browser session (no developer credentials needed)
|
|
49
|
-
|
|
50
|
-
The browser path is quick to start and useful when the user does not have X developer app credentials. It captures auth cookies from Chrome and uses them to interact with X. Chrome management uses `assistant browser chrome launch` and `assistant browser chrome minimize` CLI commands internally.
|
|
51
|
-
|
|
52
|
-
- Supports: **all operations** (post, reply, timeline, search, home, bookmarks, notifications, likes, followers, following, media)
|
|
53
|
-
- Setup: Run `assistant x refresh` to open Chrome and capture session cookies automatically.
|
|
54
|
-
- Set the strategy: `assistant config set twitter.operationStrategy browser`
|
|
55
|
-
- **Session storage**: Session cookies are stored in the encrypted credential store under the key `twitter:session:cookies`. You can inspect the stored session with `assistant credentials inspect twitter:session:cookies`.
|
|
56
|
-
|
|
57
|
-
### Auto mode (default)
|
|
58
|
-
|
|
59
|
-
When the strategy is `auto` (the default), the router tries OAuth first for supported operations if credentials are available, then falls back to the browser path. This gives the best of both worlds without requiring manual switching.
|
|
60
|
-
|
|
61
|
-
- Set auto mode: `assistant config set twitter.operationStrategy auto`
|
|
62
43
|
|
|
63
44
|
## First-Use Decision Flow
|
|
64
45
|
|
|
65
|
-
When the user triggers a Twitter operation and no
|
|
46
|
+
When the user triggers a Twitter operation and no connection has been configured yet, follow these steps:
|
|
66
47
|
|
|
67
48
|
1. **Check current status:**
|
|
68
49
|
|
|
69
50
|
```bash
|
|
70
|
-
# Check strategy
|
|
71
|
-
assistant config get twitter.operationStrategy
|
|
72
|
-
|
|
73
51
|
# Check if OAuth token is available
|
|
74
52
|
assistant oauth token twitter --json
|
|
75
53
|
|
|
76
|
-
# Check
|
|
77
|
-
assistant
|
|
54
|
+
# Check integration mode
|
|
55
|
+
assistant config get twitter.integrationMode
|
|
78
56
|
```
|
|
79
57
|
|
|
80
|
-
|
|
58
|
+
2. **Determine the best path:**
|
|
59
|
+
- If managed mode prerequisites are met (integration mode is `managed`, assistant is registered with the platform, API key is present, and the owner has connected their Twitter account), use managed mode.
|
|
60
|
+
- Otherwise, guide the user through OAuth setup.
|
|
81
61
|
|
|
82
|
-
|
|
83
|
-
-
|
|
84
|
-
- **Browser session**: Quick to start, no developer credentials needed. Supports all operations including reading timelines and searching. Set up with `assistant x refresh`.
|
|
85
|
-
|
|
86
|
-
3. **Ask the user which they prefer.** Do not choose for them.
|
|
87
|
-
|
|
88
|
-
4. **Execute setup for the chosen path:**
|
|
62
|
+
3. **Execute setup for the chosen path:**
|
|
63
|
+
- If managed: Confirm prerequisites are satisfied. Managed mode requires no local setup beyond platform registration.
|
|
89
64
|
- If OAuth: Collect the credentials in-chat using the secure credential prompt, then connect. Follow the **OAuth Setup Sequence** below.
|
|
90
|
-
- If browser: Run `assistant x refresh` to capture session cookies from Chrome.
|
|
91
65
|
|
|
92
66
|
### OAuth Setup Sequence
|
|
93
67
|
|
|
94
|
-
When the user chooses OAuth, collect their X developer credentials conversationally using the secure UI. The OAuth flow delegates to the generic connect orchestrator, which resolves the Twitter provider profile, computes scopes via policy, opens the X authorization page
|
|
68
|
+
When the user chooses OAuth, collect their X developer credentials conversationally using the secure UI. The OAuth flow delegates to the generic connect orchestrator, which resolves the Twitter provider profile, computes scopes via policy, opens the X authorization page, verifies the user's identity, and stores tokens. The orchestrator also manages stale refresh-token cleanup and enforces integration-mode guards.
|
|
95
69
|
|
|
96
70
|
1. **Collect the Client ID securely:**
|
|
97
71
|
Call `credential_store` with `action: "prompt"`, `service: "integration:twitter"`, `field: "client_id"`, `label: "X (Twitter) OAuth Client ID"`, `description: "Enter the Client ID from your X Developer App"`, and `placeholder: "your-client-id"`.
|
|
@@ -100,113 +74,72 @@ When the user chooses OAuth, collect their X developer credentials conversationa
|
|
|
100
74
|
Ask the user if their X app uses a confidential client (has a Client Secret). If yes, call `credential_store` with `action: "prompt"`, `service: "integration:twitter"`, `field: "client_secret"`, `label: "X (Twitter) OAuth Client Secret"`, `description: "Enter the Client Secret from your X Developer App (leave blank if using a public client)"`, and `placeholder: "your-client-secret"`.
|
|
101
75
|
|
|
102
76
|
3. **Initiate the OAuth flow:**
|
|
103
|
-
Trigger the Twitter auth start flow. The connect orchestrator resolves the Twitter provider profile, computes scopes via policy, opens the X authorization page
|
|
77
|
+
Trigger the Twitter auth start flow. The connect orchestrator resolves the Twitter provider profile, computes scopes via policy, opens the X authorization page, verifies the user's identity, and stores tokens. Wait for the auth result.
|
|
104
78
|
|
|
105
79
|
4. **Confirm success:**
|
|
106
80
|
Tell the user: "Great, your X account is connected! You can always update these credentials from the Settings page."
|
|
107
81
|
|
|
108
|
-
5. **Set the preferred strategy:**
|
|
109
|
-
```bash
|
|
110
|
-
assistant config set twitter.operationStrategy <oauth|browser|auto>
|
|
111
|
-
```
|
|
112
|
-
|
|
113
82
|
## Failure Recovery Flow
|
|
114
83
|
|
|
115
84
|
When a Twitter operation fails, follow these steps:
|
|
116
85
|
|
|
117
86
|
1. **Detect the failure type from the error output:**
|
|
118
|
-
- `
|
|
119
|
-
- `
|
|
120
|
-
- `
|
|
121
|
-
- `
|
|
122
|
-
- `
|
|
123
|
-
- `proxyErrorCode: "
|
|
124
|
-
- `proxyErrorCode: "
|
|
125
|
-
- `proxyErrorCode: "
|
|
126
|
-
- `proxyErrorCode: "missing_assistant_api_key"` — managed mode: the assistant is not bootstrapped.
|
|
127
|
-
- `proxyErrorCode: "missing_platform_assistant_id"` — managed mode: the assistant is not registered with the platform.
|
|
87
|
+
- `OAuth is not configured` -- the user chose OAuth but credentials are not set up.
|
|
88
|
+
- `Twitter API error (401)` -- OAuth token may be expired or revoked.
|
|
89
|
+
- `Cannot connect to assistant` -- the Vellum assistant is not running.
|
|
90
|
+
- `proxyErrorCode: "owner_credential_required"` -- managed mode: the assistant owner has not connected their X account on the platform.
|
|
91
|
+
- `proxyErrorCode: "owner_only"` -- managed mode: the current user is not the assistant owner.
|
|
92
|
+
- `proxyErrorCode: "auth_failure"` or `"upstream_failure"` -- managed mode: platform token issue, reconnect Twitter on the platform.
|
|
93
|
+
- `proxyErrorCode: "missing_assistant_api_key"` -- managed mode: the assistant is not bootstrapped.
|
|
94
|
+
- `proxyErrorCode: "missing_platform_assistant_id"` -- managed mode: the assistant is not registered with the platform.
|
|
128
95
|
|
|
129
96
|
2. **Explain the likely cause clearly** to the user.
|
|
130
97
|
|
|
131
|
-
3. **Suggest
|
|
132
|
-
- If
|
|
133
|
-
- If OAuth failed or is not configured: suggest using the browser path with `assistant config set twitter.operationStrategy browser` and `assistant x refresh`.
|
|
134
|
-
- If the operation is unsupported via OAuth: explain that this write operation is not yet supported via OAuth, and suggest using the browser path with `assistant config set twitter.operationStrategy browser`.
|
|
98
|
+
3. **Suggest recovery steps:**
|
|
99
|
+
- If OAuth failed or is not configured: suggest reconnecting OAuth credentials or checking that the X developer app credentials are correct.
|
|
135
100
|
- If managed mode failed with a credential or ownership error: explain the specific issue and guide the user to resolve it on the platform (connect Twitter, sign in as owner, etc.).
|
|
136
101
|
|
|
137
|
-
4. **Offer concrete steps to switch:**
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
# Switch to the other strategy
|
|
141
|
-
assistant config set twitter.operationStrategy <oauth|browser|auto>
|
|
142
|
-
|
|
143
|
-
# If switching to browser, refresh the session
|
|
144
|
-
assistant x refresh
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## Strategy Management Commands
|
|
148
|
-
|
|
149
|
-
```bash
|
|
150
|
-
# Check current strategy
|
|
151
|
-
assistant config get twitter.operationStrategy
|
|
152
|
-
|
|
153
|
-
# Set strategy to OAuth, browser, or auto
|
|
154
|
-
assistant config set twitter.operationStrategy oauth
|
|
155
|
-
assistant config set twitter.operationStrategy browser
|
|
156
|
-
assistant config set twitter.operationStrategy auto
|
|
157
|
-
|
|
158
|
-
# Check full status (session, OAuth, and strategy info)
|
|
159
|
-
assistant x status --json
|
|
160
|
-
```
|
|
161
|
-
|
|
162
102
|
## Posting
|
|
163
103
|
|
|
164
|
-
Before posting, check the integration mode
|
|
104
|
+
Before posting, check the integration mode:
|
|
165
105
|
|
|
166
106
|
```bash
|
|
167
|
-
# 1. Check integration mode
|
|
168
107
|
MODE=$(assistant config get twitter.integrationMode)
|
|
169
|
-
|
|
170
|
-
# 2. Get the configured strategy
|
|
171
|
-
STRATEGY=$(assistant config get twitter.operationStrategy)
|
|
172
|
-
# If not set, default to "auto"
|
|
173
108
|
```
|
|
174
109
|
|
|
175
|
-
Then post based on the mode
|
|
110
|
+
Then post based on the mode:
|
|
176
111
|
|
|
177
112
|
```bash
|
|
178
|
-
# Managed mode
|
|
179
|
-
assistant x post "The post text here" --
|
|
113
|
+
# Managed mode -- route through platform proxy (no local token needed):
|
|
114
|
+
assistant x post "The post text here" --managed
|
|
180
115
|
|
|
181
|
-
# With OAuth token
|
|
116
|
+
# With OAuth token:
|
|
182
117
|
TOKEN=$(assistant oauth token twitter)
|
|
183
|
-
assistant x post "The post text here" --
|
|
184
|
-
|
|
185
|
-
# With browser-only strategy:
|
|
186
|
-
assistant x post "The post text here" --strategy browser
|
|
118
|
+
assistant x post "The post text here" --oauth-token "$TOKEN"
|
|
187
119
|
```
|
|
188
120
|
|
|
189
|
-
When `twitter.integrationMode` is `managed`, always use `--
|
|
121
|
+
When `twitter.integrationMode` is `managed`, always use `--managed`. The platform proxy handles authentication.
|
|
190
122
|
|
|
191
123
|
Returns JSON with `ok`, `tweetId`, `text`, `url`, and `pathUsed` fields. Share the URL with the user so they can verify the post. For managed mode errors, the response includes `proxyErrorCode` and `retryable` fields.
|
|
192
124
|
|
|
193
125
|
## Replying
|
|
194
126
|
|
|
195
|
-
Same setup as posting
|
|
127
|
+
Same setup as posting -- check integration mode first, then:
|
|
196
128
|
|
|
197
129
|
```bash
|
|
198
130
|
# Managed mode:
|
|
199
|
-
assistant x reply <tweetUrl> "The reply text here" --
|
|
131
|
+
assistant x reply <tweetUrl> "The reply text here" --managed
|
|
200
132
|
|
|
201
|
-
# Local OAuth
|
|
202
|
-
assistant
|
|
133
|
+
# Local OAuth:
|
|
134
|
+
TOKEN=$(assistant oauth token twitter)
|
|
135
|
+
assistant x reply <tweetUrl> "The reply text here" --oauth-token "$TOKEN"
|
|
203
136
|
```
|
|
204
137
|
|
|
205
138
|
The first argument is a tweet URL (e.g. `https://x.com/user/status/123456`) or a bare tweet ID.
|
|
206
139
|
|
|
207
140
|
## Reading
|
|
208
141
|
|
|
209
|
-
Read
|
|
142
|
+
Read operations are available in managed mode. They route through the platform proxy.
|
|
210
143
|
|
|
211
144
|
### User timeline
|
|
212
145
|
|
|
@@ -227,65 +160,21 @@ Returns the focal tweet and its reply thread.
|
|
|
227
160
|
### Search
|
|
228
161
|
|
|
229
162
|
```bash
|
|
230
|
-
assistant x search "query" [--
|
|
163
|
+
assistant x search "query" [--product Top|Latest|People|Media]
|
|
231
164
|
```
|
|
232
165
|
|
|
233
|
-
### Home timeline
|
|
234
|
-
|
|
235
|
-
```bash
|
|
236
|
-
assistant x home [--count N]
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
### Bookmarks
|
|
240
|
-
|
|
241
|
-
```bash
|
|
242
|
-
assistant x bookmarks [--count N]
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
### Notifications
|
|
246
|
-
|
|
247
|
-
```bash
|
|
248
|
-
assistant x notifications [--count N]
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
Returns `notifications` array with `id`, `message`, `timestamp`, `url`.
|
|
252
|
-
|
|
253
|
-
### Likes
|
|
254
|
-
|
|
255
|
-
```bash
|
|
256
|
-
assistant x likes <screenName> [--count N]
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### Followers / Following
|
|
260
|
-
|
|
261
|
-
```bash
|
|
262
|
-
assistant x followers <screenName> [--count N]
|
|
263
|
-
assistant x following <screenName> [--count N]
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
Returns `user` and `followers`/`following` array (userId, screenName, name).
|
|
267
|
-
|
|
268
|
-
### Media
|
|
269
|
-
|
|
270
|
-
```bash
|
|
271
|
-
assistant x media <screenName> [--count N]
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
Returns tweets that contain media from the user's profile.
|
|
275
|
-
|
|
276
166
|
## Workflows
|
|
277
167
|
|
|
278
168
|
### Check Mentions
|
|
279
169
|
|
|
280
170
|
When the user asks to check mentions, check X, or see what's happening:
|
|
281
171
|
|
|
282
|
-
1. Fetch
|
|
283
|
-
2.
|
|
284
|
-
|
|
285
|
-
- Group by type: replies to their tweets, likes, new followers, mentions
|
|
172
|
+
1. Fetch their recent tweets to see replies: `assistant x timeline <theirScreenName> --count 10 --json`
|
|
173
|
+
2. Summarize what needs attention:
|
|
174
|
+
- Group by type: replies to their tweets, mentions
|
|
286
175
|
- For anything that looks like it needs a reply, fetch the full thread with `assistant x tweet <tweetId>` to understand context
|
|
287
|
-
- Prioritize: direct questions > mentions > engagement
|
|
288
|
-
|
|
176
|
+
- Prioritize: direct questions > mentions > engagement
|
|
177
|
+
3. For items that need replies, draft a response and ask the user to approve before sending with `assistant x reply`
|
|
289
178
|
|
|
290
179
|
Present the summary as a scannable list, not a wall of text. Lead with action items.
|
|
291
180
|
|
|
@@ -304,16 +193,14 @@ When the user wants to see how their posts are performing:
|
|
|
304
193
|
|
|
305
194
|
1. Fetch their recent tweets: `assistant x timeline <screenName> --count 20 --json`
|
|
306
195
|
2. For each tweet, note engagement signals from the text/metadata
|
|
307
|
-
3.
|
|
308
|
-
4. Summarize: which posts got traction, who's engaging, any conversations worth continuing
|
|
196
|
+
3. Summarize: which posts got traction, who's engaging, any conversations worth continuing
|
|
309
197
|
|
|
310
198
|
## Tips
|
|
311
199
|
|
|
312
200
|
- Keep posts under 280 characters
|
|
313
201
|
- All `screenName` arguments should be without the `@` prefix
|
|
314
202
|
- All commands return JSON with an `ok` field
|
|
315
|
-
- When drafting replies, match the tone of the conversation
|
|
203
|
+
- When drafting replies, match the tone of the conversation -- casual threads get casual replies
|
|
316
204
|
- Always show the user what you're about to post and get approval before sending
|
|
317
|
-
- If a browser session is expired, refresh it with `assistant x refresh` before retrying, or suggest switching to OAuth for post/reply operations
|
|
318
205
|
- If an operation fails, check `assistant x status --json` to diagnose the issue before retrying
|
|
319
206
|
- The `post` and `reply` commands include a `pathUsed` field in their response so you can tell the user which connection method was used
|
|
@@ -104,6 +104,14 @@
|
|
|
104
104
|
"label": "App Builder Multi-file",
|
|
105
105
|
"description": "Enable multi-file TSX app creation with esbuild compilation instead of single-HTML apps",
|
|
106
106
|
"defaultEnabled": false
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": "sentry-testing",
|
|
110
|
+
"scope": "macos",
|
|
111
|
+
"key": "sentry_testing_enabled",
|
|
112
|
+
"label": "Sentry Testing",
|
|
113
|
+
"description": "Show the Sentry Testing tab in Settings for triggering test crash reports and error events",
|
|
114
|
+
"defaultEnabled": false
|
|
107
115
|
}
|
|
108
116
|
]
|
|
109
117
|
}
|
|
@@ -27,6 +27,43 @@ import {
|
|
|
27
27
|
renderHistoryContent,
|
|
28
28
|
} from "./shared.js";
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* In light mode, strip heavy payloads (e.g. full HTML) from surface data
|
|
32
|
+
* but preserve the fields the client needs to parse and render the surface.
|
|
33
|
+
*/
|
|
34
|
+
function lightModeSurfaceData(s: HistorySurface): Record<string, unknown> {
|
|
35
|
+
switch (s.surfaceType) {
|
|
36
|
+
case "dynamic_page":
|
|
37
|
+
return {
|
|
38
|
+
...(s.data.preview ? { preview: s.data.preview } : {}),
|
|
39
|
+
...(s.data.appId ? { appId: s.data.appId } : {}),
|
|
40
|
+
};
|
|
41
|
+
case "card":
|
|
42
|
+
return {
|
|
43
|
+
...(typeof s.data.title === "string" ? { title: s.data.title } : {}),
|
|
44
|
+
...(typeof s.data.body === "string" ? { body: s.data.body } : {}),
|
|
45
|
+
...(typeof s.data.template === "string"
|
|
46
|
+
? { template: s.data.template }
|
|
47
|
+
: {}),
|
|
48
|
+
...(s.data.templateData ? { templateData: s.data.templateData } : {}),
|
|
49
|
+
};
|
|
50
|
+
case "document_preview":
|
|
51
|
+
return {
|
|
52
|
+
...(typeof s.data.surfaceId === "string"
|
|
53
|
+
? { surfaceId: s.data.surfaceId }
|
|
54
|
+
: {}),
|
|
55
|
+
...(typeof s.data.title === "string" ? { title: s.data.title } : {}),
|
|
56
|
+
...(typeof s.data.content === "string"
|
|
57
|
+
? { content: s.data.content }
|
|
58
|
+
: {}),
|
|
59
|
+
};
|
|
60
|
+
default:
|
|
61
|
+
// For other types (list, table, form, confirmation, etc.),
|
|
62
|
+
// preserve the full data — these are generally small.
|
|
63
|
+
return s.data;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
30
67
|
export function handleHistoryRequest(
|
|
31
68
|
msg: HistoryRequest,
|
|
32
69
|
ctx: HandlerContext,
|
|
@@ -190,7 +227,9 @@ export function handleHistoryRequest(
|
|
|
190
227
|
})
|
|
191
228
|
: m.toolCalls;
|
|
192
229
|
|
|
193
|
-
// In light mode, strip
|
|
230
|
+
// In light mode, strip heavy payloads but keep essential fields so
|
|
231
|
+
// the client can still parse and render surfaces (e.g. card title/body,
|
|
232
|
+
// dynamic_page preview, document_preview metadata).
|
|
194
233
|
const filteredSurfaces =
|
|
195
234
|
m.surfaces.length > 0
|
|
196
235
|
? includeSurfaceData
|
|
@@ -199,14 +238,7 @@ export function handleHistoryRequest(
|
|
|
199
238
|
surfaceId: s.surfaceId,
|
|
200
239
|
surfaceType: s.surfaceType,
|
|
201
240
|
title: s.title,
|
|
202
|
-
data:
|
|
203
|
-
...(s.surfaceType === "dynamic_page"
|
|
204
|
-
? {
|
|
205
|
-
...(s.data.preview ? { preview: s.data.preview } : {}),
|
|
206
|
-
...(s.data.appId ? { appId: s.data.appId } : {}),
|
|
207
|
-
}
|
|
208
|
-
: {}),
|
|
209
|
-
} as Record<string, unknown>,
|
|
241
|
+
data: lightModeSurfaceData(s),
|
|
210
242
|
...(s.actions ? { actions: s.actions } : {}),
|
|
211
243
|
...(s.display ? { display: s.display } : {}),
|
|
212
244
|
}))
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -93,7 +93,10 @@ import {
|
|
|
93
93
|
registerMessagingProviders,
|
|
94
94
|
registerWatcherProviders,
|
|
95
95
|
} from "./providers-setup.js";
|
|
96
|
-
import {
|
|
96
|
+
import {
|
|
97
|
+
handleRideShotgunStart,
|
|
98
|
+
handleRideShotgunStop,
|
|
99
|
+
} from "./ride-shotgun-handler.js";
|
|
97
100
|
import { seedInterfaceFiles } from "./seed-files.js";
|
|
98
101
|
import { DaemonServer } from "./server.js";
|
|
99
102
|
import { initSlashPairingContext } from "./session-slash.js";
|
|
@@ -439,23 +442,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
439
442
|
undoLastMessage(sessionId, server.getHandlerContext()),
|
|
440
443
|
regenerateResponse: (sessionId) => {
|
|
441
444
|
let hubChain: Promise<void> = Promise.resolve();
|
|
442
|
-
const session = server
|
|
443
|
-
.getHandlerContext()
|
|
444
|
-
.sessions.get(sessionId);
|
|
445
445
|
const sendEvent = (event: ServerMessage) => {
|
|
446
|
-
// Skip state-signal events only when the session has an
|
|
447
|
-
// onStateSignal listener wired (set in conversation-routes.ts),
|
|
448
|
-
// since that listener already publishes them to the hub.
|
|
449
|
-
// Without the listener (e.g. IPC-originated sessions regenerated
|
|
450
|
-
// via HTTP), we must let these events through to avoid silent loss.
|
|
451
|
-
if (
|
|
452
|
-
session?.hasStateSignalListener &&
|
|
453
|
-
"type" in event &&
|
|
454
|
-
(event.type === "assistant_activity_state" ||
|
|
455
|
-
event.type === "confirmation_state_changed")
|
|
456
|
-
) {
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
446
|
const ae = buildAssistantEvent(
|
|
460
447
|
DAEMON_INTERNAL_ASSISTANT_ID,
|
|
461
448
|
event,
|
|
@@ -18,12 +18,6 @@ export interface AppsListRequest {
|
|
|
18
18
|
type: "apps_list";
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export interface HomeBaseGetRequest {
|
|
22
|
-
type: "home_base_get";
|
|
23
|
-
/** If true, daemon ensures a durable Home Base link exists before responding. */
|
|
24
|
-
ensureLinked?: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
21
|
export interface AppOpenRequest {
|
|
28
22
|
type: "app_open_request";
|
|
29
23
|
appId: string;
|
|
@@ -176,23 +170,6 @@ export interface AppsListResponse {
|
|
|
176
170
|
}>;
|
|
177
171
|
}
|
|
178
172
|
|
|
179
|
-
export interface HomeBaseGetResponse {
|
|
180
|
-
type: "home_base_get_response";
|
|
181
|
-
homeBase: {
|
|
182
|
-
appId: string;
|
|
183
|
-
source: string;
|
|
184
|
-
starterTasks: string[];
|
|
185
|
-
onboardingTasks: string[];
|
|
186
|
-
preview: {
|
|
187
|
-
title: string;
|
|
188
|
-
subtitle: string;
|
|
189
|
-
description: string;
|
|
190
|
-
icon: string;
|
|
191
|
-
metrics: Array<{ label: string; value: string }>;
|
|
192
|
-
};
|
|
193
|
-
} | null;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
173
|
export interface SharedAppsListResponse {
|
|
197
174
|
type: "shared_apps_list_response";
|
|
198
175
|
apps: Array<{
|
|
@@ -361,7 +338,6 @@ export interface AppFilesChanged {
|
|
|
361
338
|
export type _AppsClientMessages =
|
|
362
339
|
| AppDataRequest
|
|
363
340
|
| AppsListRequest
|
|
364
|
-
| HomeBaseGetRequest
|
|
365
341
|
| AppOpenRequest
|
|
366
342
|
| SharedAppsListRequest
|
|
367
343
|
| AppDeleteRequest
|
|
@@ -386,7 +362,6 @@ export type _AppsClientMessages =
|
|
|
386
362
|
export type _AppsServerMessages =
|
|
387
363
|
| AppDataResponse
|
|
388
364
|
| AppsListResponse
|
|
389
|
-
| HomeBaseGetResponse
|
|
390
365
|
| SharedAppsListResponse
|
|
391
366
|
| AppDeleteResponse
|
|
392
367
|
| SharedAppDeleteResponse
|
|
@@ -36,13 +36,10 @@ export interface TwitterIntegrationConfigRequest {
|
|
|
36
36
|
| "set_mode"
|
|
37
37
|
| "set_local_client"
|
|
38
38
|
| "clear_local_client"
|
|
39
|
-
| "disconnect"
|
|
40
|
-
| "get_strategy"
|
|
41
|
-
| "set_strategy";
|
|
39
|
+
| "disconnect";
|
|
42
40
|
mode?: "local_byo" | "managed";
|
|
43
41
|
clientId?: string;
|
|
44
42
|
clientSecret?: string;
|
|
45
|
-
strategy?: string;
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
export interface TelegramConfigRequest {
|
|
@@ -159,9 +156,6 @@ export interface TwitterIntegrationConfigResponse {
|
|
|
159
156
|
localClientConfigured: boolean;
|
|
160
157
|
connected: boolean;
|
|
161
158
|
accountInfo?: string;
|
|
162
|
-
strategy?: "oauth" | "browser" | "auto";
|
|
163
|
-
/** Whether the user has explicitly set a strategy (vs. relying on the default 'auto'). */
|
|
164
|
-
strategyConfigured?: boolean;
|
|
165
159
|
error?: string;
|
|
166
160
|
}
|
|
167
161
|
|
|
@@ -304,7 +304,12 @@ export interface HistoryResponseSurface {
|
|
|
304
304
|
surfaceType: string;
|
|
305
305
|
title?: string;
|
|
306
306
|
data: Record<string, unknown>;
|
|
307
|
-
actions?: Array<{
|
|
307
|
+
actions?: Array<{
|
|
308
|
+
id: string;
|
|
309
|
+
label: string;
|
|
310
|
+
style?: string;
|
|
311
|
+
data?: Record<string, unknown>;
|
|
312
|
+
}>;
|
|
308
313
|
display?: string;
|
|
309
314
|
}
|
|
310
315
|
|
|
@@ -23,6 +23,8 @@ export interface SurfaceAction {
|
|
|
23
23
|
id: string;
|
|
24
24
|
label: string;
|
|
25
25
|
style?: "primary" | "secondary" | "destructive";
|
|
26
|
+
/** Optional data payload returned to the daemon when this action is clicked. */
|
|
27
|
+
data?: Record<string, unknown>;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export interface CardSurfaceData {
|