@rynx-ai/plugin-channel-lark 0.1.10 → 0.1.11-beta.10

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 (36) hide show
  1. package/.rynx-plugin/plugin.json +25 -0
  2. package/dist/cli-mirror/mirror-index.d.ts +4 -2
  3. package/dist/cli-mirror/mirror-index.js +10 -2
  4. package/dist/host-adapters.d.ts +26 -13
  5. package/dist/host-adapters.js +146 -44
  6. package/dist/index.d.ts +4 -4
  7. package/dist/index.js +3 -3
  8. package/dist/lark-config-store.d.ts +30 -0
  9. package/dist/lark-config-store.js +135 -0
  10. package/dist/lark-runtime-state-store.d.ts +18 -0
  11. package/dist/lark-runtime-state-store.js +95 -0
  12. package/dist/lark-setup.d.ts +27 -5
  13. package/dist/lark-setup.js +24 -7
  14. package/dist/lark.d.ts +9 -2
  15. package/dist/lark.js +45 -80
  16. package/dist/runtime.d.ts +5 -8
  17. package/dist/runtime.js +131312 -141
  18. package/dist/settings.d.ts +7 -0
  19. package/dist/{runtime.mjs → settings.js} +6631 -10535
  20. package/package.json +27 -10
  21. package/ui/assets/index-CGUVAMsv.css +1 -0
  22. package/ui/assets/index-D8Amoe6a.js +201 -0
  23. package/ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  24. package/ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  25. package/ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  26. package/ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  27. package/ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  28. package/ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  29. package/ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  30. package/ui/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  31. package/ui/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  32. package/ui/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  33. package/ui/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  34. package/ui/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  35. package/ui/index.html +13 -0
  36. package/rynx-plugin.json +0 -42
@@ -0,0 +1,25 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "id": "lark",
4
+ "version": "0.1.11",
5
+ "displayName": "Lark",
6
+ "description": "Lark and Feishu channel integration managed by the plugin.",
7
+ "apiRange": "^1.0.0",
8
+ "runtime": {
9
+ "entry": "./dist/runtime.js"
10
+ },
11
+ "channels": [
12
+ {
13
+ "id": "lark",
14
+ "displayName": "Lark"
15
+ }
16
+ ],
17
+ "settings": {
18
+ "ui": {
19
+ "entry": "./ui/index.html"
20
+ },
21
+ "background": {
22
+ "entry": "./dist/settings.js"
23
+ }
24
+ }
25
+ }
@@ -1,4 +1,4 @@
1
- import type { CodexSessionStore, ConversationSessionStore } from "@rynx-ai/core";
1
+ import type { CodexSessionStore, ConversationSessionStore, SessionRegistry } from "@rynx-ai/core";
2
2
  import type { LarkConversationDirectory } from "../lark-conversation-directory.js";
3
3
  /**
4
4
  * Resolves a Codex thread id (the rollout-file UUID) back to the Feishu
@@ -32,10 +32,12 @@ export declare class CodexMirrorIndex {
32
32
  private readonly codexSessionStore;
33
33
  private readonly conversationSessionStore;
34
34
  private readonly conversationDirectory;
35
- constructor({ codexSessionStore, conversationSessionStore, conversationDirectory, }: {
35
+ private readonly sessionRegistry;
36
+ constructor({ codexSessionStore, conversationSessionStore, conversationDirectory, sessionRegistry, }: {
36
37
  codexSessionStore: Pick<CodexSessionStore, "listAll" | "findByCodexSessionId">;
37
38
  conversationSessionStore: Pick<ConversationSessionStore, "findKeyBySessionId">;
38
39
  conversationDirectory: Pick<LarkConversationDirectory, "get">;
40
+ sessionRegistry: Pick<SessionRegistry, "get">;
39
41
  });
40
42
  /** codexSessionId → the full send target, or null when the thread isn't
41
43
  * bound, its session isn't routed, or the conversation was never recorded. */
@@ -2,10 +2,12 @@ export class CodexMirrorIndex {
2
2
  codexSessionStore;
3
3
  conversationSessionStore;
4
4
  conversationDirectory;
5
- constructor({ codexSessionStore, conversationSessionStore, conversationDirectory, }) {
5
+ sessionRegistry;
6
+ constructor({ codexSessionStore, conversationSessionStore, conversationDirectory, sessionRegistry, }) {
6
7
  this.codexSessionStore = codexSessionStore;
7
8
  this.conversationSessionStore = conversationSessionStore;
8
9
  this.conversationDirectory = conversationDirectory;
10
+ this.sessionRegistry = sessionRegistry;
9
11
  }
10
12
  /** codexSessionId → the full send target, or null when the thread isn't
11
13
  * bound, its session isn't routed, or the conversation was never recorded. */
@@ -22,6 +24,10 @@ export class CodexMirrorIndex {
22
24
  if (!directory) {
23
25
  return null;
24
26
  }
27
+ const session = this.sessionRegistry.get(record.localThreadId);
28
+ if (!session) {
29
+ return null;
30
+ }
25
31
  return {
26
32
  codexSessionId,
27
33
  localThreadId: record.localThreadId,
@@ -29,7 +35,9 @@ export class CodexMirrorIndex {
29
35
  chatId: directory.chatId,
30
36
  threadId: directory.threadId,
31
37
  participants: directory.participants ?? [],
32
- model: record.model,
38
+ // A null model means the provider CLI owns model selection. The mirror
39
+ // still needs a stable string label for transcript normalization.
40
+ model: session.execution.model ?? session.execution.provider,
33
41
  };
34
42
  }
35
43
  /** All Codex threads currently bound to a Feishu conversation (those whose
@@ -1,13 +1,20 @@
1
- import { type AgentCapabilities, type AgentRuntimeId, type CapabilityResult, type ConversationRuntime, type ConversationStreamRequest, type ModelListResponse, type SessionEvent, type ThreadGoal } from "@rynx-ai/core";
1
+ import { type AgentCapabilities, type AgentRuntimeId, type CapabilityResult, type ConversationSessionRecord, type ConversationStreamRequest, type ModelListResponse, type SessionExecution, type SessionEvent, type ThreadGoal } from "@rynx-ai/core";
2
2
  import type { PluginAgentSummary, PluginHostClient, PluginResolvedSessionExecution, PluginSessionExecutionSnapshot } from "@rynx-ai/plugin-sdk";
3
3
  import { FileLarkActiveSessionStore } from "./lark-session-store.js";
4
- type LarkConversationRuntime = Pick<ConversationRuntime, "runLive">;
4
+ import type { LarkConversationRuntime } from "./lark.js";
5
+ interface HostSessionLaunchMeta {
6
+ sourceSessionId?: string;
7
+ agent?: string;
8
+ provider?: AgentRuntimeId;
9
+ model?: string;
10
+ reasoningEffort?: string;
11
+ }
5
12
  /** Slow consumers fail the run instead of retaining an unbounded event stream. */
6
13
  export declare const LARK_RUN_EVENT_QUEUE_MAX_ITEMS = 256;
7
14
  export declare const LARK_RUN_EVENT_QUEUE_MAX_BYTES: number;
8
15
  /**
9
- * ConversationRuntime-shaped adapter backed exclusively by capability-checked
10
- * Host RPC. The plugin never receives the daemon's runtime, abort controller,
16
+ * ConversationRuntime-shaped adapter backed exclusively by the typed Host API.
17
+ * The plugin never receives the daemon's runtime, abort controller,
11
18
  * steer controller, or session bus.
12
19
  */
13
20
  export declare class HostConversationRuntime implements LarkConversationRuntime {
@@ -16,12 +23,12 @@ export declare class HostConversationRuntime implements LarkConversationRuntime
16
23
  runLive(input: ConversationStreamRequest): Promise<{
17
24
  provider: "codex" | "traex" | "claude";
18
25
  threadId: string;
19
- model: string;
26
+ model: string | null;
20
27
  authSource: "codex_cli" | "traex_cli" | "claude_cli";
21
28
  generator: AsyncGenerator<SessionEvent, any, any>;
22
29
  }>;
23
30
  }
24
- /** Agent command adapter used by /models, /goal, and /fork. */
31
+ /** Agent command adapter used by /models and /goal. Session fork is a Core RPC. */
25
32
  export declare class HostAgentCapabilities implements AgentCapabilities {
26
33
  private readonly host;
27
34
  constructor(host: PluginHostClient);
@@ -29,7 +36,6 @@ export declare class HostAgentCapabilities implements AgentCapabilities {
29
36
  getGoal(sessionId: string): Promise<CapabilityResult<ThreadGoal | null>>;
30
37
  setGoal(sessionId: string, objective: string): Promise<CapabilityResult>;
31
38
  clearGoal(sessionId: string): Promise<CapabilityResult>;
32
- forkSession(fromSessionId: string, toSessionId: string): Promise<CapabilityResult>;
33
39
  }
34
40
  /** Narrow agent catalog + first-turn execution resolver backed by Host RPC. */
35
41
  export declare class HostAgentCatalog {
@@ -46,10 +52,14 @@ export declare class HostAgentCatalog {
46
52
  export declare class HostSessionProvisioner {
47
53
  private readonly host;
48
54
  readonly instanceId: string;
55
+ private readonly resolvedExecutions;
49
56
  constructor(host: PluginHostClient, instanceId: string);
50
- provision(operationId: string): Promise<string>;
57
+ provision(operationId: string, session?: HostSessionLaunchMeta): Promise<string>;
58
+ takeExecution(sessionId: string): SessionExecution | undefined;
59
+ existingExecution(sessionId: string): Promise<SessionExecution | undefined>;
51
60
  allocationOperationId(sessionKey: string, previousSessionId?: string): string;
52
61
  forkOperationId(fromSessionId: string, newSessionKey: string): string;
62
+ fork(operationId: string, sourceSessionId: string, title?: string): Promise<string>;
53
63
  setTitle(sessionId: string, title: string): Promise<void>;
54
64
  }
55
65
  /**
@@ -61,6 +71,7 @@ export declare class HostSessionProvisioner {
61
71
  export declare class HostProvisionedLarkSessionStore extends FileLarkActiveSessionStore {
62
72
  private readonly operation;
63
73
  private mutationTail;
74
+ private readonly instanceDefaultAgent?;
64
75
  constructor(options: {
65
76
  filePath: string;
66
77
  provisioner: HostSessionProvisioner;
@@ -69,12 +80,14 @@ export declare class HostProvisionedLarkSessionStore extends FileLarkActiveSessi
69
80
  });
70
81
  private readonly provisioner;
71
82
  ensure(sessionKey: string): Promise<import("@rynx-ai/core").EnsureSessionResult>;
72
- set(sessionKey: string, sessionId: string, cwd?: string): Promise<import("@rynx-ai/core").ConversationSessionRecord>;
83
+ set(sessionKey: string, sessionId: string, cwd?: string): Promise<ConversationSessionRecord>;
84
+ get(sessionKey: string): Promise<ConversationSessionRecord | null>;
73
85
  rotate(sessionKey: string): Promise<import("@rynx-ai/core").RotateSessionResult>;
74
- bindCwd(sessionKey: string, cwd: string): Promise<import("@rynx-ai/core").ConversationSessionRecord>;
75
- applySettings(sessionKey: string, settings: Parameters<FileLarkActiveSessionStore["applySettings"]>[1]): Promise<import("@rynx-ai/core").ConversationSessionRecord>;
76
- setAgent(sessionKey: string, agentName: string): Promise<import("@rynx-ai/core").ConversationSessionRecord>;
77
- freeze(sessionKey: string, execution: Parameters<FileLarkActiveSessionStore["freeze"]>[1]): Promise<import("@rynx-ai/core").ConversationSessionRecord>;
86
+ bindCwd(sessionKey: string, cwd: string): Promise<ConversationSessionRecord>;
87
+ applySettings(sessionKey: string, settings: Parameters<FileLarkActiveSessionStore["applySettings"]>[1]): Promise<ConversationSessionRecord>;
88
+ setAgent(sessionKey: string, agentName: string): Promise<ConversationSessionRecord>;
89
+ freeze(sessionKey: string, execution: Parameters<FileLarkActiveSessionStore["freeze"]>[1]): Promise<ConversationSessionRecord>;
90
+ private pinResolvedExecution;
78
91
  private withAllocation;
79
92
  private runMutation;
80
93
  }
@@ -6,8 +6,8 @@ import { FileLarkActiveSessionStore, } from "./lark-session-store.js";
6
6
  export const LARK_RUN_EVENT_QUEUE_MAX_ITEMS = 256;
7
7
  export const LARK_RUN_EVENT_QUEUE_MAX_BYTES = 8 * 1024 * 1024;
8
8
  /**
9
- * ConversationRuntime-shaped adapter backed exclusively by capability-checked
10
- * Host RPC. The plugin never receives the daemon's runtime, abort controller,
9
+ * ConversationRuntime-shaped adapter backed exclusively by the typed Host API.
10
+ * The plugin never receives the daemon's runtime, abort controller,
11
11
  * steer controller, or session bus.
12
12
  */
13
13
  export class HostConversationRuntime {
@@ -37,7 +37,7 @@ export class HostConversationRuntime {
37
37
  const interrupt = () => {
38
38
  if (!runId || terminal)
39
39
  return;
40
- void this.host.call("session.interrupt", { runId }).catch(() => undefined);
40
+ void this.host.sessions.interrupt({ runId }).catch(() => undefined);
41
41
  };
42
42
  const accept = (event, payload) => {
43
43
  if (!runId) {
@@ -86,17 +86,11 @@ export class HostConversationRuntime {
86
86
  unsubscribeEvents();
87
87
  let started;
88
88
  try {
89
- started = await this.host.call("session.run", {
89
+ started = await this.host.sessions.run({
90
90
  sessionId: input.threadId,
91
+ model: input.model?.trim() || null,
92
+ reasoningEffort: input.reasoningEffort?.trim() || null,
91
93
  message: input.message,
92
- ...(input.provider ? { provider: input.provider } : {}),
93
- ...(input.model ? { model: input.model } : {}),
94
- ...(input.reasoningEffort ? { reasoningEffort: input.reasoningEffort } : {}),
95
- ...(input.cwd ? { cwd: input.cwd } : {}),
96
- ...(input.agentName ? { agent: input.agentName } : {}),
97
- ...(input.developerInstructions
98
- ? { developerInstructions: input.developerInstructions }
99
- : {}),
100
94
  });
101
95
  runId = started.runId;
102
96
  if (earlyOverflow) {
@@ -117,7 +111,7 @@ export class HostConversationRuntime {
117
111
  const unsteer = input.steer?.subscribe((text) => {
118
112
  if (!runId || terminal)
119
113
  return;
120
- void this.host.call("session.steer", { runId, text }).catch(() => undefined);
114
+ void this.host.sessions.steer({ runId, text }).catch(() => undefined);
121
115
  });
122
116
  input.signal?.addEventListener("abort", interrupt, { once: true });
123
117
  const generator = (async function* () {
@@ -142,26 +136,23 @@ export class HostConversationRuntime {
142
136
  };
143
137
  }
144
138
  }
145
- /** Agent command adapter used by /models, /goal, and /fork. */
139
+ /** Agent command adapter used by /models and /goal. Session fork is a Core RPC. */
146
140
  export class HostAgentCapabilities {
147
141
  host;
148
142
  constructor(host) {
149
143
  this.host = host;
150
144
  }
151
145
  listModels(runtime) {
152
- return this.host.call("session.models", runtime ? { runtime } : {});
146
+ return this.host.sessions.listModels(runtime ? { runtime } : undefined);
153
147
  }
154
148
  getGoal(sessionId) {
155
- return this.host.call("session.goal.get", { sessionId });
149
+ return this.host.sessions.goals.get({ sessionId });
156
150
  }
157
151
  setGoal(sessionId, objective) {
158
- return this.host.call("session.goal.set", { sessionId, objective });
152
+ return this.host.sessions.goals.set({ sessionId, objective });
159
153
  }
160
154
  clearGoal(sessionId) {
161
- return this.host.call("session.goal.clear", { sessionId });
162
- }
163
- forkSession(fromSessionId, toSessionId) {
164
- return this.host.call("session.fork", { fromSessionId, toSessionId });
155
+ return this.host.sessions.goals.clear({ sessionId });
165
156
  }
166
157
  }
167
158
  /** Narrow agent catalog + first-turn execution resolver backed by Host RPC. */
@@ -171,41 +162,82 @@ export class HostAgentCatalog {
171
162
  this.host = host;
172
163
  }
173
164
  list() {
174
- return this.host.call("agent.catalog.list", {});
165
+ return this.host.agents.list();
175
166
  }
176
167
  get(id) {
177
- return this.host.call("agent.catalog.get", { id });
168
+ return this.host.agents.get({ id });
178
169
  }
179
170
  resolveExecution(input) {
180
- return this.host.call("agent.execution.resolve", input);
171
+ return this.host.agents.resolveExecution(input);
181
172
  }
182
173
  }
183
174
  /** Daemon-owned session allocator. operationId must identify one allocation attempt durably. */
184
175
  export class HostSessionProvisioner {
185
176
  host;
186
177
  instanceId;
178
+ resolvedExecutions = new Map();
187
179
  constructor(host, instanceId) {
188
180
  this.host = host;
189
181
  this.instanceId = instanceId;
190
182
  }
191
- async provision(operationId) {
192
- const result = await this.host.call("session.ensure", {
183
+ async provision(operationId, session = {}) {
184
+ const result = await this.host.sessions.ensure({
193
185
  scopeId: this.instanceId,
194
186
  operationId,
195
- session: {},
187
+ session,
196
188
  });
197
189
  if (!result?.sessionId)
198
190
  throw new Error("host did not return a provisioned session id");
191
+ this.resolvedExecutions.set(result.sessionId, {
192
+ runtime: result.execution.provider,
193
+ ...(result.execution.model ? { model: result.execution.model } : {}),
194
+ ...(result.execution.reasoningEffort
195
+ ? { reasoningEffort: result.execution.reasoningEffort }
196
+ : {}),
197
+ ...(session.agent ? { agent: session.agent } : {}),
198
+ });
199
199
  return result.sessionId;
200
200
  }
201
+ takeExecution(sessionId) {
202
+ const execution = this.resolvedExecutions.get(sessionId);
203
+ this.resolvedExecutions.delete(sessionId);
204
+ return execution;
205
+ }
206
+ async existingExecution(sessionId) {
207
+ try {
208
+ const session = await this.host.sessions.get({ sessionId });
209
+ return {
210
+ runtime: session.execution.provider,
211
+ ...(session.execution.model ? { model: session.execution.model } : {}),
212
+ ...(session.execution.reasoningEffort
213
+ ? { reasoningEffort: session.execution.reasoningEffort }
214
+ : {}),
215
+ };
216
+ }
217
+ catch (error) {
218
+ if (hostErrorCode(error) === "SESSION_NOT_FOUND")
219
+ return undefined;
220
+ throw error;
221
+ }
222
+ }
201
223
  allocationOperationId(sessionKey, previousSessionId) {
202
224
  return stableOperationId("allocate", this.instanceId, sessionKey, previousSessionId ?? "initial");
203
225
  }
204
226
  forkOperationId(fromSessionId, newSessionKey) {
205
227
  return stableOperationId("fork", this.instanceId, fromSessionId, newSessionKey);
206
228
  }
229
+ async fork(operationId, sourceSessionId, title) {
230
+ const result = await this.host.sessions.fork({
231
+ sourceSessionId,
232
+ operationId,
233
+ ...(title ? { title } : {}),
234
+ });
235
+ if (!result?.sessionId)
236
+ throw new Error("host did not return a forked session id");
237
+ return result.sessionId;
238
+ }
207
239
  async setTitle(sessionId, title) {
208
- await this.host.call("session.meta.setTitle", { sessionId, title });
240
+ await this.host.sessions.setTitle({ sessionId, title });
209
241
  }
210
242
  }
211
243
  /**
@@ -217,6 +249,7 @@ export class HostSessionProvisioner {
217
249
  export class HostProvisionedLarkSessionStore extends FileLarkActiveSessionStore {
218
250
  operation;
219
251
  mutationTail = Promise.resolve();
252
+ instanceDefaultAgent;
220
253
  constructor(options) {
221
254
  const operation = new AsyncLocalStorage();
222
255
  super({
@@ -224,52 +257,94 @@ export class HostProvisionedLarkSessionStore extends FileLarkActiveSessionStore
224
257
  defaultAgent: options.defaultAgent,
225
258
  now: options.now,
226
259
  createSessionId: async () => {
227
- const operationId = operation.getStore();
228
- if (!operationId)
260
+ const allocation = operation.getStore();
261
+ if (!allocation)
229
262
  throw new Error("session allocation is missing its stable operation id");
230
- return options.provisioner.provision(operationId);
263
+ return options.provisioner.provision(allocation.operationId, allocation.session);
231
264
  },
232
265
  });
233
266
  this.operation = operation;
234
267
  this.provisioner = options.provisioner;
268
+ this.instanceDefaultAgent = options.defaultAgent;
235
269
  }
236
270
  provisioner;
237
271
  async ensure(sessionKey) {
238
272
  return this.runMutation(async () => {
239
273
  const current = await super.get(sessionKey);
240
- if (current)
241
- return { ...current, source: "existing" };
242
- return this.withAllocation(sessionKey, undefined, () => super.ensure(sessionKey));
274
+ const existingExecution = current
275
+ ? await this.provisioner.existingExecution(current.sessionId)
276
+ : undefined;
277
+ if (current && existingExecution) {
278
+ const pinned = current.runtime
279
+ ? current
280
+ : await super.freeze(sessionKey, existingExecution);
281
+ return { ...current, ...pinned, source: "existing" };
282
+ }
283
+ if (current) {
284
+ const replacement = await this.withAllocation(sessionKey, current.sessionId, launchMeta(current), () => super.rotate(sessionKey));
285
+ return {
286
+ ...await this.pinResolvedExecution(sessionKey, replacement),
287
+ source: "new",
288
+ };
289
+ }
290
+ const created = await this.withAllocation(sessionKey, undefined, this.instanceDefaultAgent ? { agent: this.instanceDefaultAgent } : {}, () => super.ensure(sessionKey));
291
+ return this.pinResolvedExecution(sessionKey, created);
243
292
  });
244
293
  }
245
294
  async set(sessionKey, sessionId, cwd) {
246
- return this.runMutation(() => super.set(sessionKey, sessionId, cwd));
295
+ return this.runMutation(async () => this.pinResolvedExecution(sessionKey, await super.set(sessionKey, sessionId, cwd)));
247
296
  }
248
- async rotate(sessionKey) {
297
+ async get(sessionKey) {
249
298
  return this.runMutation(async () => {
250
299
  const current = await super.get(sessionKey);
251
- return this.withAllocation(sessionKey, current?.sessionId, () => super.rotate(sessionKey));
300
+ if (!current)
301
+ return current;
302
+ const existingExecution = await this.provisioner.existingExecution(current.sessionId);
303
+ if (existingExecution) {
304
+ return current.runtime ? current : super.freeze(sessionKey, existingExecution);
305
+ }
306
+ const replacement = await this.withAllocation(sessionKey, current.sessionId, launchMeta(current), () => super.rotate(sessionKey));
307
+ return this.pinResolvedExecution(sessionKey, replacement);
252
308
  });
253
309
  }
254
- async bindCwd(sessionKey, cwd) {
310
+ async rotate(sessionKey) {
255
311
  return this.runMutation(async () => {
256
312
  const current = await super.get(sessionKey);
257
- return this.withAllocation(sessionKey, current?.sessionId, () => super.bindCwd(sessionKey, cwd));
313
+ const rotated = await this.withAllocation(sessionKey, current?.sessionId, launchMeta(current), () => super.rotate(sessionKey));
314
+ return this.pinResolvedExecution(sessionKey, rotated);
258
315
  });
259
316
  }
317
+ async bindCwd(sessionKey, cwd) {
318
+ void sessionKey;
319
+ void cwd;
320
+ throw new Error("working directory is managed by the Session Project snapshot");
321
+ }
260
322
  async applySettings(sessionKey, settings) {
261
323
  return this.runMutation(async () => {
262
324
  if (!settings.rotate && (await super.get(sessionKey))) {
263
325
  return super.applySettings(sessionKey, settings);
264
326
  }
265
327
  const current = await super.get(sessionKey);
266
- return this.withAllocation(sessionKey, current?.sessionId, () => super.applySettings(sessionKey, settings));
328
+ const providerChanged = Boolean(settings.runtime && settings.runtime !== current?.runtime);
329
+ const updated = await this.withAllocation(sessionKey, current?.sessionId, launchMeta({
330
+ ...(providerChanged
331
+ ? { provider: settings.runtime }
332
+ : (current?.agent ?? this.instanceDefaultAgent)
333
+ ? { agent: current?.agent ?? this.instanceDefaultAgent }
334
+ : settings.runtime
335
+ ? { provider: settings.runtime }
336
+ : {}),
337
+ ...(settings.model ? { model: settings.model } : {}),
338
+ ...(settings.reasoningEffort ? { reasoningEffort: settings.reasoningEffort } : {}),
339
+ }), () => super.applySettings(sessionKey, settings));
340
+ return this.pinResolvedExecution(sessionKey, updated);
267
341
  });
268
342
  }
269
343
  async setAgent(sessionKey, agentName) {
270
344
  return this.runMutation(async () => {
271
345
  const current = await super.get(sessionKey);
272
- return this.withAllocation(sessionKey, current?.sessionId, () => super.setAgent(sessionKey, agentName));
346
+ const updated = await this.withAllocation(sessionKey, current?.sessionId, { agent: agentName }, () => super.setAgent(sessionKey, agentName));
347
+ return this.pinResolvedExecution(sessionKey, updated);
273
348
  });
274
349
  }
275
350
  async freeze(sessionKey, execution) {
@@ -277,11 +352,22 @@ export class HostProvisionedLarkSessionStore extends FileLarkActiveSessionStore
277
352
  const current = await super.get(sessionKey);
278
353
  if (current)
279
354
  return super.freeze(sessionKey, execution);
280
- return this.withAllocation(sessionKey, undefined, () => super.freeze(sessionKey, execution));
355
+ const frozen = await this.withAllocation(sessionKey, undefined, launchMeta(execution), () => super.freeze(sessionKey, execution));
356
+ return this.pinResolvedExecution(sessionKey, frozen);
281
357
  });
282
358
  }
283
- withAllocation(sessionKey, previousSessionId, run) {
284
- return this.operation.run(this.provisioner.allocationOperationId(sessionKey, previousSessionId), run);
359
+ async pinResolvedExecution(sessionKey, record) {
360
+ const execution = this.provisioner.takeExecution(record.sessionId);
361
+ if (!execution)
362
+ return record;
363
+ const frozen = await super.freeze(sessionKey, execution);
364
+ return { ...record, ...frozen };
365
+ }
366
+ withAllocation(sessionKey, previousSessionId, session, run) {
367
+ return this.operation.run({
368
+ operationId: this.provisioner.allocationOperationId(sessionKey, previousSessionId),
369
+ session,
370
+ }, run);
285
371
  }
286
372
  async runMutation(run) {
287
373
  const previous = this.mutationTail;
@@ -298,6 +384,22 @@ export class HostProvisionedLarkSessionStore extends FileLarkActiveSessionStore
298
384
  }
299
385
  }
300
386
  }
387
+ function launchMeta(input) {
388
+ if (!input)
389
+ return {};
390
+ return {
391
+ ...(input.agent ? { agent: input.agent } : {}),
392
+ ...(input.provider ? { provider: input.provider } : {}),
393
+ ...(input.model ? { model: input.model } : {}),
394
+ ...(input.reasoningEffort ? { reasoningEffort: input.reasoningEffort } : {}),
395
+ };
396
+ }
397
+ function hostErrorCode(error) {
398
+ return error && typeof error === "object" && "code" in error &&
399
+ typeof error.code === "string"
400
+ ? error.code
401
+ : undefined;
402
+ }
301
403
  export function stableOperationId(kind, ...parts) {
302
404
  const digest = createHash("sha256").update(parts.join("\0")).digest("hex");
303
405
  return `${kind}-${digest}`;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- /** Public Lark plugin API. The manifest executable is the self-contained
2
- * `dist/runtime.mjs`, loaded only by @rynx-ai/plugin-runner; this module exports
3
- * no legacy in-process `RynxPlugin` manifest or daemon Channel factory. */
1
+ /** Public Lark plugin API. Rynx loads the manifest's self-contained runtime
2
+ * module; this package exports no in-process daemon Channel factory. */
4
3
  export { LocalLarkBotRuntime } from "./lark.js";
5
- export { authorizeLark, larkConfigSchema, LARK_BOT_SCOPES, LARK_BOT_EVENTS, } from "./lark-setup.js";
4
+ export { authorizeLark, larkConfigSchema, LARK_BOT_SCOPES, LARK_BOT_EVENTS, type LarkAuthorizationMaterial, type LarkSetupField, } from "./lark-setup.js";
6
5
  export { activate, createLarkPluginRuntime, LARK_CHANNEL_ID, type LarkPluginRuntimeOptions, } from "./runtime.js";
6
+ export { createLarkPluginSettings, type LarkPluginSettingsOptions, } from "./settings.js";
7
7
  export { HostAgentCapabilities, HostAgentCatalog, HostConversationRuntime, HostProvisionedLarkSessionStore, HostSessionProvisioner, stableOperationId, } from "./host-adapters.js";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- /** Public Lark plugin API. The manifest executable is the self-contained
2
- * `dist/runtime.mjs`, loaded only by @rynx-ai/plugin-runner; this module exports
3
- * no legacy in-process `RynxPlugin` manifest or daemon Channel factory. */
1
+ /** Public Lark plugin API. Rynx loads the manifest's self-contained runtime
2
+ * module; this package exports no in-process daemon Channel factory. */
4
3
  export { LocalLarkBotRuntime } from "./lark.js";
5
4
  export { authorizeLark, larkConfigSchema, LARK_BOT_SCOPES, LARK_BOT_EVENTS, } from "./lark-setup.js";
6
5
  export { activate, createLarkPluginRuntime, LARK_CHANNEL_ID, } from "./runtime.js";
6
+ export { createLarkPluginSettings, } from "./settings.js";
7
7
  export { HostAgentCapabilities, HostAgentCatalog, HostConversationRuntime, HostProvisionedLarkSessionStore, HostSessionProvisioner, stableOperationId, } from "./host-adapters.js";
@@ -0,0 +1,30 @@
1
+ import type { PluginExecutionDefaults } from "@rynx-ai/plugin-sdk";
2
+ export interface LarkInstanceConfig {
3
+ id: string;
4
+ name: string;
5
+ enabled: boolean;
6
+ appId: string;
7
+ appSecret: string;
8
+ defaultAgent?: string;
9
+ executionDefaults: PluginExecutionDefaults;
10
+ }
11
+ export declare class LarkConfigStore {
12
+ private readonly file;
13
+ constructor(dataDir: string);
14
+ list(): LarkInstanceConfig[];
15
+ get(id: string): LarkInstanceConfig | undefined;
16
+ upsert(input: {
17
+ id?: string;
18
+ name: string;
19
+ enabled: boolean;
20
+ appId: string;
21
+ appSecret?: string;
22
+ defaultAgent?: string | null;
23
+ runtime?: PluginExecutionDefaults["runtime"];
24
+ allowedRoots?: string[];
25
+ }): LarkInstanceConfig;
26
+ remove(id: string): boolean;
27
+ setEnabled(id: string, enabled: boolean): LarkInstanceConfig;
28
+ private read;
29
+ private write;
30
+ }