@alfe.ai/claude-code-host 0.1.2 → 0.1.4

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.
@@ -463,6 +463,7 @@ var SessionDriver = class {
463
463
  "--allowedTools",
464
464
  allowed
465
465
  ];
466
+ if (params.model) args.push("--model", params.model);
466
467
  if (params.claudeSessionId) args.push("--resume", params.claudeSessionId);
467
468
  if (params.mcpConfigPath) args.push("--mcp-config", params.mcpConfigPath);
468
469
  if (params.appendSystemPrompt) args.push("--append-system-prompt", params.appendSystemPrompt);
@@ -792,8 +793,20 @@ const VOICE_REPLY_SYSTEM_PROMPT = [
792
793
  ].join("\n");
793
794
  /** Cross-agent tool steering (every turn). Mirrors CROSS_AGENT_TOOLS_SYSTEM_PROMPT. */
794
795
  const CROSS_AGENT_TOOLS_SYSTEM_PROMPT = ["Cross-agent messaging: to contact another Alfe agent (IDs like agt_…), use list_agents to discover and message_agent to send.", "The runtime's built-in session tools do NOT know Alfe agent IDs and will fail — never use them to reach another agent."].join("\n");
795
- /** Persona files read from the workspace, in precedence order (first wins as lead). */
796
- const PERSONA_FILES = ["SOUL.md", "IDENTITY.md"];
796
+ /**
797
+ * Short, AUTHORITATIVE persona pointer for `--append-system-prompt`. The full
798
+ * persona (SOUL/IDENTITY/AGENTS/…) is delivered as the workspace `CLAUDE.md`,
799
+ * which Claude Code auto-loads every turn — so we do NOT re-dump it into the
800
+ * system prompt (doing both double-injected the persona, wasting tokens and
801
+ * risking drift). This system-level pointer just establishes the framing that
802
+ * CLAUDE.md-as-project-memory can't guarantee on its own: this is an Alfe chat
803
+ * agent, not a coding session.
804
+ */
805
+ const PERSONA_POINTER_SYSTEM_PROMPT = [
806
+ "You are an Alfe agent replying to a user over a chat bridge — NOT an interactive coding session.",
807
+ "Your identity, persona, voice, and operating instructions live in CLAUDE.md and the other files in your workspace.",
808
+ "Fully embody that persona in how you speak and act; respond AS that agent, not as a generic coding assistant."
809
+ ].join("\n");
797
810
  /**
798
811
  * One FIFO backlog PER claude-session key. Unlike openclaw-chat's single
799
812
  * per-daemon queue, the Claude Code host spawns a distinct `claude -p` per
@@ -982,7 +995,7 @@ var ClaudeCodeHost = class {
982
995
  } catch (err) {
983
996
  this.log.warn(`[claude-host] memoryLoadContext failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
984
997
  }
985
- const appendSystemPrompt = await this.buildSystemPrompt(memoryContext, isVoice);
998
+ const appendSystemPrompt = this.buildSystemPrompt(memoryContext, isVoice);
986
999
  const claudeSessionId = this.claudeSessionIds.get(bareSessionId) ?? null;
987
1000
  const driver = this.makeDriver();
988
1001
  this.runningTurns.set(sessionKey, req.id);
@@ -995,6 +1008,7 @@ var ClaudeCodeHost = class {
995
1008
  cwd: this.cfg.workspace,
996
1009
  ...this.cfg.mcpConfigPath ? { mcpConfigPath: this.cfg.mcpConfigPath } : {},
997
1010
  ...appendSystemPrompt ? { appendSystemPrompt } : {},
1011
+ ...this.cfg.model ? { model: this.cfg.model } : {},
998
1012
  onEmission: (emission) => {
999
1013
  this.forwardEmission(runId, sessionKey, emission);
1000
1014
  }
@@ -1105,7 +1119,11 @@ var ClaudeCodeHost = class {
1105
1119
  }
1106
1120
  async handleSessionsList(req) {
1107
1121
  const params = req.params;
1108
- const sessions = await listSessions(params);
1122
+ const sessions = await listSessions({
1123
+ channel: params.channel,
1124
+ tenantId: params.tenantId,
1125
+ userId: params.userId
1126
+ });
1109
1127
  this.chat.sendResponse(req.id, true, { sessions });
1110
1128
  }
1111
1129
  async handleSessionsGet(req) {
@@ -1139,23 +1157,13 @@ var ClaudeCodeHost = class {
1139
1157
  activity: session.activity ?? []
1140
1158
  });
1141
1159
  }
1142
- async buildSystemPrompt(memoryContext, isVoice) {
1143
- const parts = [];
1144
- const persona = await this.readPersona();
1145
- if (persona) parts.push(persona);
1160
+ buildSystemPrompt(memoryContext, isVoice) {
1161
+ const parts = [PERSONA_POINTER_SYSTEM_PROMPT];
1146
1162
  if (memoryContext) parts.push(`# Relevant memory\n\n${memoryContext}`);
1147
1163
  if (isVoice) parts.push(VOICE_REPLY_SYSTEM_PROMPT);
1148
1164
  parts.push(CROSS_AGENT_TOOLS_SYSTEM_PROMPT);
1149
1165
  return parts.join("\n\n");
1150
1166
  }
1151
- async readPersona() {
1152
- const chunks = [];
1153
- for (const file of PERSONA_FILES) try {
1154
- const text = await (0, node_fs_promises.readFile)((0, node_path.join)(this.cfg.workspace, file), "utf-8");
1155
- if (text.trim()) chunks.push(text.trim());
1156
- } catch {}
1157
- return chunks.join("\n\n");
1158
- }
1159
1167
  async buildHistoryPreamble(bareSessionId) {
1160
1168
  const session = await getSession(bareSessionId);
1161
1169
  if (!session || session.messages.length === 0) return "";
@@ -1165,7 +1173,7 @@ var ClaudeCodeHost = class {
1165
1173
  }
1166
1174
  async ensureSession(bareSessionId, params) {
1167
1175
  if (await getSession(bareSessionId)) return;
1168
- await createSession(bareSessionId, params.agentId, params.clientType ?? "chat", params.tenantId, params.identityId ?? params.userId);
1176
+ await createSession(bareSessionId, params.agentId, params.clientType ?? "chat", params.tenantId, params.userId);
1169
1177
  }
1170
1178
  async loadClaudeSessionIds() {
1171
1179
  try {
@@ -1217,7 +1225,8 @@ function resolveHostConfig() {
1217
1225
  apiUrl: resolved.apiUrl,
1218
1226
  workspace: resolved.workspacePath,
1219
1227
  mcpConfigPath: (0, node_path.join)((0, node_os.homedir)(), ".claude-code", "mcp-config.json"),
1220
- claudeSessionsPath: (0, node_path.join)((0, node_os.homedir)(), ".alfe", "claude-code", "sessions.json")
1228
+ claudeSessionsPath: (0, node_path.join)((0, node_os.homedir)(), ".alfe", "claude-code", "sessions.json"),
1229
+ ...process.env.ALFE_DEFAULT_MODEL?.trim() ? { model: process.env.ALFE_DEFAULT_MODEL.trim() } : {}
1221
1230
  };
1222
1231
  }
1223
1232
  //#endregion
@@ -463,6 +463,7 @@ var SessionDriver = class {
463
463
  "--allowedTools",
464
464
  allowed
465
465
  ];
466
+ if (params.model) args.push("--model", params.model);
466
467
  if (params.claudeSessionId) args.push("--resume", params.claudeSessionId);
467
468
  if (params.mcpConfigPath) args.push("--mcp-config", params.mcpConfigPath);
468
469
  if (params.appendSystemPrompt) args.push("--append-system-prompt", params.appendSystemPrompt);
@@ -792,8 +793,20 @@ const VOICE_REPLY_SYSTEM_PROMPT = [
792
793
  ].join("\n");
793
794
  /** Cross-agent tool steering (every turn). Mirrors CROSS_AGENT_TOOLS_SYSTEM_PROMPT. */
794
795
  const CROSS_AGENT_TOOLS_SYSTEM_PROMPT = ["Cross-agent messaging: to contact another Alfe agent (IDs like agt_…), use list_agents to discover and message_agent to send.", "The runtime's built-in session tools do NOT know Alfe agent IDs and will fail — never use them to reach another agent."].join("\n");
795
- /** Persona files read from the workspace, in precedence order (first wins as lead). */
796
- const PERSONA_FILES = ["SOUL.md", "IDENTITY.md"];
796
+ /**
797
+ * Short, AUTHORITATIVE persona pointer for `--append-system-prompt`. The full
798
+ * persona (SOUL/IDENTITY/AGENTS/…) is delivered as the workspace `CLAUDE.md`,
799
+ * which Claude Code auto-loads every turn — so we do NOT re-dump it into the
800
+ * system prompt (doing both double-injected the persona, wasting tokens and
801
+ * risking drift). This system-level pointer just establishes the framing that
802
+ * CLAUDE.md-as-project-memory can't guarantee on its own: this is an Alfe chat
803
+ * agent, not a coding session.
804
+ */
805
+ const PERSONA_POINTER_SYSTEM_PROMPT = [
806
+ "You are an Alfe agent replying to a user over a chat bridge — NOT an interactive coding session.",
807
+ "Your identity, persona, voice, and operating instructions live in CLAUDE.md and the other files in your workspace.",
808
+ "Fully embody that persona in how you speak and act; respond AS that agent, not as a generic coding assistant."
809
+ ].join("\n");
797
810
  /**
798
811
  * One FIFO backlog PER claude-session key. Unlike openclaw-chat's single
799
812
  * per-daemon queue, the Claude Code host spawns a distinct `claude -p` per
@@ -982,7 +995,7 @@ var ClaudeCodeHost = class {
982
995
  } catch (err) {
983
996
  this.log.warn(`[claude-host] memoryLoadContext failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
984
997
  }
985
- const appendSystemPrompt = await this.buildSystemPrompt(memoryContext, isVoice);
998
+ const appendSystemPrompt = this.buildSystemPrompt(memoryContext, isVoice);
986
999
  const claudeSessionId = this.claudeSessionIds.get(bareSessionId) ?? null;
987
1000
  const driver = this.makeDriver();
988
1001
  this.runningTurns.set(sessionKey, req.id);
@@ -995,6 +1008,7 @@ var ClaudeCodeHost = class {
995
1008
  cwd: this.cfg.workspace,
996
1009
  ...this.cfg.mcpConfigPath ? { mcpConfigPath: this.cfg.mcpConfigPath } : {},
997
1010
  ...appendSystemPrompt ? { appendSystemPrompt } : {},
1011
+ ...this.cfg.model ? { model: this.cfg.model } : {},
998
1012
  onEmission: (emission) => {
999
1013
  this.forwardEmission(runId, sessionKey, emission);
1000
1014
  }
@@ -1105,7 +1119,11 @@ var ClaudeCodeHost = class {
1105
1119
  }
1106
1120
  async handleSessionsList(req) {
1107
1121
  const params = req.params;
1108
- const sessions = await listSessions(params);
1122
+ const sessions = await listSessions({
1123
+ channel: params.channel,
1124
+ tenantId: params.tenantId,
1125
+ userId: params.userId
1126
+ });
1109
1127
  this.chat.sendResponse(req.id, true, { sessions });
1110
1128
  }
1111
1129
  async handleSessionsGet(req) {
@@ -1139,23 +1157,13 @@ var ClaudeCodeHost = class {
1139
1157
  activity: session.activity ?? []
1140
1158
  });
1141
1159
  }
1142
- async buildSystemPrompt(memoryContext, isVoice) {
1143
- const parts = [];
1144
- const persona = await this.readPersona();
1145
- if (persona) parts.push(persona);
1160
+ buildSystemPrompt(memoryContext, isVoice) {
1161
+ const parts = [PERSONA_POINTER_SYSTEM_PROMPT];
1146
1162
  if (memoryContext) parts.push(`# Relevant memory\n\n${memoryContext}`);
1147
1163
  if (isVoice) parts.push(VOICE_REPLY_SYSTEM_PROMPT);
1148
1164
  parts.push(CROSS_AGENT_TOOLS_SYSTEM_PROMPT);
1149
1165
  return parts.join("\n\n");
1150
1166
  }
1151
- async readPersona() {
1152
- const chunks = [];
1153
- for (const file of PERSONA_FILES) try {
1154
- const text = await readFile(join(this.cfg.workspace, file), "utf-8");
1155
- if (text.trim()) chunks.push(text.trim());
1156
- } catch {}
1157
- return chunks.join("\n\n");
1158
- }
1159
1167
  async buildHistoryPreamble(bareSessionId) {
1160
1168
  const session = await getSession(bareSessionId);
1161
1169
  if (!session || session.messages.length === 0) return "";
@@ -1165,7 +1173,7 @@ var ClaudeCodeHost = class {
1165
1173
  }
1166
1174
  async ensureSession(bareSessionId, params) {
1167
1175
  if (await getSession(bareSessionId)) return;
1168
- await createSession(bareSessionId, params.agentId, params.clientType ?? "chat", params.tenantId, params.identityId ?? params.userId);
1176
+ await createSession(bareSessionId, params.agentId, params.clientType ?? "chat", params.tenantId, params.userId);
1169
1177
  }
1170
1178
  async loadClaudeSessionIds() {
1171
1179
  try {
@@ -1217,7 +1225,8 @@ function resolveHostConfig() {
1217
1225
  apiUrl: resolved.apiUrl,
1218
1226
  workspace: resolved.workspacePath,
1219
1227
  mcpConfigPath: join(homedir(), ".claude-code", "mcp-config.json"),
1220
- claudeSessionsPath: join(homedir(), ".alfe", "claude-code", "sessions.json")
1228
+ claudeSessionsPath: join(homedir(), ".alfe", "claude-code", "sessions.json"),
1229
+ ...process.env.ALFE_DEFAULT_MODEL?.trim() ? { model: process.env.ALFE_DEFAULT_MODEL.trim() } : {}
1221
1230
  };
1222
1231
  }
1223
1232
  //#endregion
package/dist/index.d.cts CHANGED
@@ -521,6 +521,67 @@ declare class AgentApiClient {
521
521
  scopes: string;
522
522
  }[];
523
523
  }>;
524
+ /**
525
+ * Pattern A: provider-parameterized multi-account credential fetch for the
526
+ * social connectors (Bluesky, and the approval-gated backlog: X, Meta,
527
+ * Threads, LinkedIn, Pinterest, TikTok, Reddit, YouTube).
528
+ *
529
+ * Unlike the bespoke `getGithubAccounts()` / `getXeroAccounts()` shapes,
530
+ * this returns a UNIFORM normalized account shape so `@alfe.ai/social-mcp`'s
531
+ * shared driver can require a single `account` selector on every
532
+ * credential-touching tool regardless of platform. The backend
533
+ * `api-agents/{provider}/accounts` route is already provider-generic; this
534
+ * is the client-side normalization the plan (`do-we-need-any-moonlit-toucan`
535
+ * Phase 0, step 5) calls for.
536
+ *
537
+ * `accountIdentifier` is the stable per-account selector the LLM should
538
+ * pass back (for Bluesky: the account DID). `accessToken` carries whatever
539
+ * the provider's `buildCredentialsResponse` bundles (for Bluesky: the JSON
540
+ * session bundle — the driver parses the `accessJwt` out of it, or reads the
541
+ * top-level `accessJwt` from `providerMetadata`-adjacent fields). Everything
542
+ * else the driver needs for routing (handle, pdsHost, did, …) is on
543
+ * `providerMetadata`.
544
+ *
545
+ * Token refresh is delegated to connect (never done in-plugin) via the
546
+ * per-account route `POST /agent/connect/{provider}/accounts/{accountIdentifier}/refresh`
547
+ * — call `refreshSocialAccount(provider, accountIdentifier)`. (The non-account
548
+ * `POST /agent/connect/{provider}/refresh` route refreshes the provider's
549
+ * PRIMARY connection, which is wrong under multi-account Pattern A.)
550
+ */
551
+ getSocialAccounts(provider: string): Promise<{
552
+ provider: string;
553
+ accounts: {
554
+ connectionId: string;
555
+ accountIdentifier: string;
556
+ displayName: string | null;
557
+ accessToken: string;
558
+ providerMetadata: Record<string, unknown>;
559
+ connectedAt: string;
560
+ }[];
561
+ }>;
562
+ /**
563
+ * Pattern A: refresh a specific social Connection by its stable
564
+ * `accountIdentifier` (for Bluesky: the account DID) via the
565
+ * provider-generic per-account refresh route. The counterpart to
566
+ * `getSocialAccounts(provider)`; `@alfe.ai/social-mcp` calls this on a
567
+ * 401/ExpiredToken from the platform PDS/API, then re-fetches accounts to
568
+ * pick up the rotated bundle.
569
+ *
570
+ * Refresh itself is ALWAYS delegated to connect — the plugin never calls
571
+ * the platform's own refresh XRPC (e.g. `com.atproto.server.refreshSession`)
572
+ * because connect owns the encrypted refresh token + rotation persistence
573
+ * (Bluesky rotates the refreshJwt; a missed rotation kills the connection
574
+ * after one refresh). The returned `accessToken` is whatever the provider's
575
+ * `refreshToken` hook re-bundled (for Bluesky: the JSON session bundle with
576
+ * the fresh `accessJwt`) — callers typically ignore it and re-fetch via
577
+ * `getSocialAccounts` for a consistent shape.
578
+ */
579
+ refreshSocialAccount(provider: string, accountIdentifier: string): Promise<{
580
+ accountIdentifier: string;
581
+ accessToken: string;
582
+ accessTokenExpiresAt: string;
583
+ expiresAt: string;
584
+ }>;
524
585
  /**
525
586
  * @deprecated Returns a single primary credential blob (legacy "pick-the-
526
587
  * default-connection" shape). Use `getXeroAccounts()` for the multi-
@@ -756,6 +817,45 @@ declare class AgentApiClient {
756
817
  accessTokenExpiresAt: string;
757
818
  expiresAt: string;
758
819
  }>;
820
+ /**
821
+ * @deprecated Returns a single primary credential blob. Use
822
+ * `getShopifyAccounts()` for the multi-account shape required by Pattern A
823
+ * (`@alfe.ai/shopify-mcp` keys per-shop on the myshopify domain).
824
+ */
825
+ getShopifyCredentials(): Promise<{
826
+ accessToken: string;
827
+ shopDomain: string;
828
+ shopGid: string;
829
+ shopName: string;
830
+ apiVersion: string;
831
+ }>;
832
+ /**
833
+ * Pattern A: multi-account credential fetch for Shopify. Returns every
834
+ * agent-scoped Shopify Connection. One OAuth grant maps to one store, so the
835
+ * stable per-call selector is the store's myshopify domain (`shopDomain`),
836
+ * NOT `accountIdentifier` — the connect provider keys `accountIdentifier` on
837
+ * the immutable shop GID (falling back to the domain), so `shopDomain` is the
838
+ * value the LLM passes and the plugin routes on.
839
+ *
840
+ * Each entry is shaped by the connect provider's `buildCredentialsResponse`:
841
+ * `{ accessToken, shopDomain, shopGid, shopName, apiVersion }` — offline
842
+ * Shopify tokens never expire, so there is NO token / expiry field and no
843
+ * refresh method (unlike Salesforce). The GraphQL Admin API authenticates
844
+ * purely on `X-Shopify-Access-Token`; no client credentials are on the wire.
845
+ */
846
+ getShopifyAccounts(): Promise<{
847
+ accounts: {
848
+ connectionId: string;
849
+ accountIdentifier: string;
850
+ displayName: string | null;
851
+ connectedAt: string;
852
+ accessToken: string;
853
+ shopDomain: string;
854
+ shopGid: string;
855
+ shopName: string;
856
+ apiVersion: string;
857
+ }[];
858
+ }>;
759
859
  /**
760
860
  * Disconnects one connected Microsoft 365 account for the agent, by its
761
861
  * `accountIdentifier`. Hits the generic per-account disconnect route
@@ -1770,6 +1870,8 @@ interface RunTurnParams {
1770
1870
  mcpConfigPath?: string;
1771
1871
  /** Persona + reply-style text appended to the system prompt. Omitted → none. */
1772
1872
  appendSystemPrompt?: string;
1873
+ /** Bare Claude model id for `--model` (subscription models only). Omitted → CLI default. */
1874
+ model?: string;
1773
1875
  /** Tool allow-list (defaults to DEFAULT_ALLOWED_TOOLS). */
1774
1876
  allowedTools?: readonly string[];
1775
1877
  /** Permission mode (defaults to DEFAULT_PERMISSION_MODE — never bypass). */
@@ -1838,6 +1940,13 @@ interface ClaudeCodeHostConfig {
1838
1940
  mcpConfigPath?: string;
1839
1941
  /** Path to persist Alfe sessionId → claude session id. */
1840
1942
  claudeSessionsPath?: string;
1943
+ /**
1944
+ * Bare Claude model id for `claude -p --model` (e.g. `claude-opus-4-8` or the
1945
+ * `opus`/`sonnet`/`haiku` aliases). Sourced from the agent's `defaultModel`
1946
+ * via `ALFE_DEFAULT_MODEL`. Omitted → the operator's `claude` CLI default.
1947
+ * NEVER provider-qualified (`anthropic/…`) — that's an OpenClaw-only shape.
1948
+ */
1949
+ model?: string;
1841
1950
  logger?: Logger;
1842
1951
  }
1843
1952
  declare class ClaudeCodeHost {
@@ -1889,7 +1998,6 @@ declare class ClaudeCodeHost {
1889
1998
  private handleSessionsList;
1890
1999
  private handleSessionsGet;
1891
2000
  private buildSystemPrompt;
1892
- private readPersona;
1893
2001
  private buildHistoryPreamble;
1894
2002
  private ensureSession;
1895
2003
  private loadClaudeSessionIds;
package/dist/index.d.ts CHANGED
@@ -521,6 +521,67 @@ declare class AgentApiClient {
521
521
  scopes: string;
522
522
  }[];
523
523
  }>;
524
+ /**
525
+ * Pattern A: provider-parameterized multi-account credential fetch for the
526
+ * social connectors (Bluesky, and the approval-gated backlog: X, Meta,
527
+ * Threads, LinkedIn, Pinterest, TikTok, Reddit, YouTube).
528
+ *
529
+ * Unlike the bespoke `getGithubAccounts()` / `getXeroAccounts()` shapes,
530
+ * this returns a UNIFORM normalized account shape so `@alfe.ai/social-mcp`'s
531
+ * shared driver can require a single `account` selector on every
532
+ * credential-touching tool regardless of platform. The backend
533
+ * `api-agents/{provider}/accounts` route is already provider-generic; this
534
+ * is the client-side normalization the plan (`do-we-need-any-moonlit-toucan`
535
+ * Phase 0, step 5) calls for.
536
+ *
537
+ * `accountIdentifier` is the stable per-account selector the LLM should
538
+ * pass back (for Bluesky: the account DID). `accessToken` carries whatever
539
+ * the provider's `buildCredentialsResponse` bundles (for Bluesky: the JSON
540
+ * session bundle — the driver parses the `accessJwt` out of it, or reads the
541
+ * top-level `accessJwt` from `providerMetadata`-adjacent fields). Everything
542
+ * else the driver needs for routing (handle, pdsHost, did, …) is on
543
+ * `providerMetadata`.
544
+ *
545
+ * Token refresh is delegated to connect (never done in-plugin) via the
546
+ * per-account route `POST /agent/connect/{provider}/accounts/{accountIdentifier}/refresh`
547
+ * — call `refreshSocialAccount(provider, accountIdentifier)`. (The non-account
548
+ * `POST /agent/connect/{provider}/refresh` route refreshes the provider's
549
+ * PRIMARY connection, which is wrong under multi-account Pattern A.)
550
+ */
551
+ getSocialAccounts(provider: string): Promise<{
552
+ provider: string;
553
+ accounts: {
554
+ connectionId: string;
555
+ accountIdentifier: string;
556
+ displayName: string | null;
557
+ accessToken: string;
558
+ providerMetadata: Record<string, unknown>;
559
+ connectedAt: string;
560
+ }[];
561
+ }>;
562
+ /**
563
+ * Pattern A: refresh a specific social Connection by its stable
564
+ * `accountIdentifier` (for Bluesky: the account DID) via the
565
+ * provider-generic per-account refresh route. The counterpart to
566
+ * `getSocialAccounts(provider)`; `@alfe.ai/social-mcp` calls this on a
567
+ * 401/ExpiredToken from the platform PDS/API, then re-fetches accounts to
568
+ * pick up the rotated bundle.
569
+ *
570
+ * Refresh itself is ALWAYS delegated to connect — the plugin never calls
571
+ * the platform's own refresh XRPC (e.g. `com.atproto.server.refreshSession`)
572
+ * because connect owns the encrypted refresh token + rotation persistence
573
+ * (Bluesky rotates the refreshJwt; a missed rotation kills the connection
574
+ * after one refresh). The returned `accessToken` is whatever the provider's
575
+ * `refreshToken` hook re-bundled (for Bluesky: the JSON session bundle with
576
+ * the fresh `accessJwt`) — callers typically ignore it and re-fetch via
577
+ * `getSocialAccounts` for a consistent shape.
578
+ */
579
+ refreshSocialAccount(provider: string, accountIdentifier: string): Promise<{
580
+ accountIdentifier: string;
581
+ accessToken: string;
582
+ accessTokenExpiresAt: string;
583
+ expiresAt: string;
584
+ }>;
524
585
  /**
525
586
  * @deprecated Returns a single primary credential blob (legacy "pick-the-
526
587
  * default-connection" shape). Use `getXeroAccounts()` for the multi-
@@ -756,6 +817,45 @@ declare class AgentApiClient {
756
817
  accessTokenExpiresAt: string;
757
818
  expiresAt: string;
758
819
  }>;
820
+ /**
821
+ * @deprecated Returns a single primary credential blob. Use
822
+ * `getShopifyAccounts()` for the multi-account shape required by Pattern A
823
+ * (`@alfe.ai/shopify-mcp` keys per-shop on the myshopify domain).
824
+ */
825
+ getShopifyCredentials(): Promise<{
826
+ accessToken: string;
827
+ shopDomain: string;
828
+ shopGid: string;
829
+ shopName: string;
830
+ apiVersion: string;
831
+ }>;
832
+ /**
833
+ * Pattern A: multi-account credential fetch for Shopify. Returns every
834
+ * agent-scoped Shopify Connection. One OAuth grant maps to one store, so the
835
+ * stable per-call selector is the store's myshopify domain (`shopDomain`),
836
+ * NOT `accountIdentifier` — the connect provider keys `accountIdentifier` on
837
+ * the immutable shop GID (falling back to the domain), so `shopDomain` is the
838
+ * value the LLM passes and the plugin routes on.
839
+ *
840
+ * Each entry is shaped by the connect provider's `buildCredentialsResponse`:
841
+ * `{ accessToken, shopDomain, shopGid, shopName, apiVersion }` — offline
842
+ * Shopify tokens never expire, so there is NO token / expiry field and no
843
+ * refresh method (unlike Salesforce). The GraphQL Admin API authenticates
844
+ * purely on `X-Shopify-Access-Token`; no client credentials are on the wire.
845
+ */
846
+ getShopifyAccounts(): Promise<{
847
+ accounts: {
848
+ connectionId: string;
849
+ accountIdentifier: string;
850
+ displayName: string | null;
851
+ connectedAt: string;
852
+ accessToken: string;
853
+ shopDomain: string;
854
+ shopGid: string;
855
+ shopName: string;
856
+ apiVersion: string;
857
+ }[];
858
+ }>;
759
859
  /**
760
860
  * Disconnects one connected Microsoft 365 account for the agent, by its
761
861
  * `accountIdentifier`. Hits the generic per-account disconnect route
@@ -1770,6 +1870,8 @@ interface RunTurnParams {
1770
1870
  mcpConfigPath?: string;
1771
1871
  /** Persona + reply-style text appended to the system prompt. Omitted → none. */
1772
1872
  appendSystemPrompt?: string;
1873
+ /** Bare Claude model id for `--model` (subscription models only). Omitted → CLI default. */
1874
+ model?: string;
1773
1875
  /** Tool allow-list (defaults to DEFAULT_ALLOWED_TOOLS). */
1774
1876
  allowedTools?: readonly string[];
1775
1877
  /** Permission mode (defaults to DEFAULT_PERMISSION_MODE — never bypass). */
@@ -1838,6 +1940,13 @@ interface ClaudeCodeHostConfig {
1838
1940
  mcpConfigPath?: string;
1839
1941
  /** Path to persist Alfe sessionId → claude session id. */
1840
1942
  claudeSessionsPath?: string;
1943
+ /**
1944
+ * Bare Claude model id for `claude -p --model` (e.g. `claude-opus-4-8` or the
1945
+ * `opus`/`sonnet`/`haiku` aliases). Sourced from the agent's `defaultModel`
1946
+ * via `ALFE_DEFAULT_MODEL`. Omitted → the operator's `claude` CLI default.
1947
+ * NEVER provider-qualified (`anthropic/…`) — that's an OpenClaw-only shape.
1948
+ */
1949
+ model?: string;
1841
1950
  logger?: Logger;
1842
1951
  }
1843
1952
  declare class ClaudeCodeHost {
@@ -1889,7 +1998,6 @@ declare class ClaudeCodeHost {
1889
1998
  private handleSessionsList;
1890
1999
  private handleSessionsGet;
1891
2000
  private buildSystemPrompt;
1892
- private readPersona;
1893
2001
  private buildHistoryPreamble;
1894
2002
  private ensureSession;
1895
2003
  private loadClaudeSessionIds;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/claude-code-host",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Chat bridge that makes a self-hosted Claude Code agent reachable from Alfe chat on the user's Claude subscription",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -19,7 +19,7 @@
19
19
  "dist"
20
20
  ],
21
21
  "dependencies": {
22
- "@alfe.ai/agent-api-client": "^0.11.0",
22
+ "@alfe.ai/agent-api-client": "^0.12.0",
23
23
  "@alfe.ai/chat": "^0.0.16",
24
24
  "@alfe.ai/config": "^0.3.0"
25
25
  },