@kafca/agentdock 0.1.7

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 (79) hide show
  1. package/README.md +70 -0
  2. package/bin/agentdock.mjs +275 -0
  3. package/build/icon.png +0 -0
  4. package/dist/renderer/assets/index-BaNpzC6d.js +482 -0
  5. package/dist/renderer/assets/index-CLWJyO0E.css +11 -0
  6. package/dist/renderer/favicon.svg +19 -0
  7. package/dist/renderer/index.html +14 -0
  8. package/dist/renderer/logo-options.svg +153 -0
  9. package/dist-electron/electron/knowledge-skill-script.test.js +125 -0
  10. package/dist-electron/electron/lac-cli.test.js +221 -0
  11. package/dist-electron/electron/local-core-refactor.test.js +1075 -0
  12. package/dist-electron/electron/main.js +163 -0
  13. package/dist-electron/electron/plugin-kernel.test.js +534 -0
  14. package/dist-electron/electron/preload.js +2 -0
  15. package/dist-electron/package.json +3 -0
  16. package/dist-electron/packages/contracts/src/index.js +18 -0
  17. package/dist-electron/packages/contracts/src/local-core.js +2 -0
  18. package/dist-electron/packages/core-sdk/src/index.js +406 -0
  19. package/dist-electron/packages/knowledge-api/src/ai-vector-provider.js +388 -0
  20. package/dist-electron/packages/knowledge-api/src/index.js +146 -0
  21. package/dist-electron/packages/knowledge-api/src/sqlite-store.js +467 -0
  22. package/dist-electron/packages/knowledge-api/src/thread-knowledge-store.js +21 -0
  23. package/dist-electron/packages/knowledge-api/test/ai-vector-provider.test.js +412 -0
  24. package/dist-electron/packages/plugin-sdk/src/index.js +2 -0
  25. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-backend.js +369 -0
  26. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-response-processor.js +110 -0
  27. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-session-coordinator.js +171 -0
  28. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-store.js +635 -0
  29. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-transport.js +190 -0
  30. package/dist-electron/services/local-ai-core/src/acp/local-core-acp-turn-coordinator.js +244 -0
  31. package/dist-electron/services/local-ai-core/src/acp/workspace-acp-permissions.js +52 -0
  32. package/dist-electron/services/local-ai-core/src/cli/lac.js +290 -0
  33. package/dist-electron/services/local-ai-core/src/gateway/local-core-lark-gateway.js +976 -0
  34. package/dist-electron/services/local-ai-core/src/gateway/local-core-weixin-gateway.js +1143 -0
  35. package/dist-electron/services/local-ai-core/src/kernel/bootstrap.js +291 -0
  36. package/dist-electron/services/local-ai-core/src/kernel/capability-registry.js +67 -0
  37. package/dist-electron/services/local-ai-core/src/kernel/diagnostics.js +27 -0
  38. package/dist-electron/services/local-ai-core/src/kernel/event-bus.js +27 -0
  39. package/dist-electron/services/local-ai-core/src/kernel/lifecycle-manager.js +85 -0
  40. package/dist-electron/services/local-ai-core/src/kernel/plugin-registry.js +60 -0
  41. package/dist-electron/services/local-ai-core/src/plugins/builtin/agent-localcore-acp-plugin.js +114 -0
  42. package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-lark-plugin.js +59 -0
  43. package/dist-electron/services/local-ai-core/src/plugins/builtin/channel-weixin-plugin.js +56 -0
  44. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-ai-vector-plugin.js +7 -0
  45. package/dist-electron/services/local-ai-core/src/plugins/builtin/knowledge-noop-plugin.js +7 -0
  46. package/dist-electron/services/local-ai-core/src/plugins/builtin/runtime-capabilities-plugin.js +62 -0
  47. package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-cron-plugin.js +49 -0
  48. package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-lark-plugin.js +38 -0
  49. package/dist-electron/services/local-ai-core/src/plugins/builtin/scheduler-weixin-plugin.js +38 -0
  50. package/dist-electron/services/local-ai-core/src/router/workspace-route-config.js +230 -0
  51. package/dist-electron/services/local-ai-core/src/router/workspace-router-types.js +2 -0
  52. package/dist-electron/services/local-ai-core/src/router/workspace-router.js +389 -0
  53. package/dist-electron/services/local-ai-core/src/runtime/local-core-controller.js +315 -0
  54. package/dist-electron/services/local-ai-core/src/runtime/local-core-runtime-state.js +282 -0
  55. package/dist-electron/services/local-ai-core/src/runtime/server.js +465 -0
  56. package/dist-electron/services/local-ai-core/src/runtime/standalone.js +38 -0
  57. package/dist-electron/services/local-ai-core/src/scheduler/adapters.js +2 -0
  58. package/dist-electron/services/local-ai-core/src/scheduler/cron-command-detector.js +70 -0
  59. package/dist-electron/services/local-ai-core/src/scheduler/cron.js +53 -0
  60. package/dist-electron/services/local-ai-core/src/scheduler/execution-policy.js +2 -0
  61. package/dist-electron/services/local-ai-core/src/scheduler/lark-execution-policies.js +66 -0
  62. package/dist-electron/services/local-ai-core/src/scheduler/lark-schedule-adapter.js +77 -0
  63. package/dist-electron/services/local-ai-core/src/scheduler/scheduled-conversation-executor.js +46 -0
  64. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-run-lifecycle.js +64 -0
  65. package/dist-electron/services/local-ai-core/src/scheduler/scheduler-service.js +133 -0
  66. package/dist-electron/services/local-ai-core/src/scheduler/weixin-execution-policies.js +66 -0
  67. package/dist-electron/services/local-ai-core/src/scheduler/weixin-schedule-adapter.js +78 -0
  68. package/dist-electron/services/local-ai-core/src/thread/workspace-thread-id.js +17 -0
  69. package/dist-electron/services/local-ai-core/src/thread/workspace-thread-mappers.js +43 -0
  70. package/dist-electron/shared/desktop.js +195 -0
  71. package/dist-electron/src/api/client.js +73 -0
  72. package/dist-electron/src/api/sessions.js +21 -0
  73. package/dist-electron/src/lib/session-utils.js +51 -0
  74. package/dist-electron/src/pages/Threads/thread-chat-model.js +246 -0
  75. package/dist-electron/src/pages/Threads/thread-chat-model.test.js +76 -0
  76. package/dist-electron/src/pages/Threads/thread-chat-permission.js +26 -0
  77. package/dist-electron/src/pages/Threads/thread-chat-permission.test.js +102 -0
  78. package/dist-electron/src/pages/Threads/thread-chat-task-state.js +65 -0
  79. package/package.json +146 -0
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLarkExecutionPolicy = createLarkExecutionPolicy;
4
+ function createLarkExecutionPolicy(job, options, resolveSameThread) {
5
+ if (job.executionMode === 'side-thread') {
6
+ return new LarkSideThreadExecutionPolicy(options);
7
+ }
8
+ return new LarkSameThreadExecutionPolicy(options, resolveSameThread);
9
+ }
10
+ class LarkSameThreadExecutionPolicy {
11
+ options;
12
+ resolveSameThread;
13
+ constructor(options, resolveSameThread) {
14
+ this.options = options;
15
+ this.resolveSameThread = resolveSameThread;
16
+ }
17
+ async resolveTarget(job) {
18
+ return {
19
+ kind: 'thread',
20
+ threadId: await this.resolveSameThread(job),
21
+ workspaceId: job.workspaceId,
22
+ platform: job.platform,
23
+ route: job.route,
24
+ };
25
+ }
26
+ beforeExecute(target) {
27
+ this.options.getChannelRuntime().muteThreadBridge?.(target.threadId);
28
+ }
29
+ afterExecute(target) {
30
+ this.options.getChannelRuntime().unmuteThreadBridge?.(target.threadId);
31
+ }
32
+ }
33
+ class LarkSideThreadExecutionPolicy {
34
+ options;
35
+ constructor(options) {
36
+ this.options = options;
37
+ }
38
+ async resolveTarget(job) {
39
+ const title = `[Scheduled] ${job.description || job.id}`;
40
+ const existing = (await this.options.workspaceRouter.listThreads(job.workspaceId))
41
+ .find((thread) => thread.title === title);
42
+ if (existing) {
43
+ return {
44
+ kind: 'thread',
45
+ threadId: existing.id,
46
+ workspaceId: job.workspaceId,
47
+ platform: job.platform,
48
+ route: job.route,
49
+ };
50
+ }
51
+ const created = await this.options.workspaceRouter.createThread(job.workspaceId, title);
52
+ return {
53
+ kind: 'thread',
54
+ threadId: created.id,
55
+ workspaceId: job.workspaceId,
56
+ platform: job.platform,
57
+ route: job.route,
58
+ };
59
+ }
60
+ beforeExecute(target) {
61
+ this.options.getChannelRuntime().muteThreadBridge?.(target.threadId);
62
+ }
63
+ afterExecute(target) {
64
+ this.options.getChannelRuntime().unmuteThreadBridge?.(target.threadId);
65
+ }
66
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LarkScheduleAdapter = void 0;
4
+ const scheduled_conversation_executor_js_1 = require("./scheduled-conversation-executor.js");
5
+ const lark_execution_policies_js_1 = require("./lark-execution-policies.js");
6
+ class LarkScheduleAdapter {
7
+ options;
8
+ executor;
9
+ deliveryTargets = ['lark'];
10
+ constructor(options) {
11
+ this.options = options;
12
+ this.executor = new scheduled_conversation_executor_js_1.ScheduledConversationExecutor({
13
+ store: options.store,
14
+ workspaceRouter: options.getWorkspaceRouter(),
15
+ });
16
+ }
17
+ supports(job) {
18
+ return job.platform === 'lark' && (job.route.type === 'channel.chat' || job.route.type === 'lark_chat');
19
+ }
20
+ async execute(context) {
21
+ const { job } = context;
22
+ const executionPolicy = (0, lark_execution_policies_js_1.createLarkExecutionPolicy)(job, {
23
+ store: this.options.store,
24
+ workspaceRouter: this.options.getWorkspaceRouter(),
25
+ getChannelRuntime: this.options.getChannelRuntime,
26
+ }, (nextJob) => this.resolveThread(nextJob));
27
+ const execution = await this.executor.execute(job, job.promptTemplate, executionPolicy);
28
+ let platformMessageId = '';
29
+ if (execution.replyText) {
30
+ const channelRuntime = this.options.getChannelRuntime();
31
+ if (!channelRuntime.sendScheduledMessage) {
32
+ throw new Error('Lark channel runtime does not support scheduled delivery.');
33
+ }
34
+ platformMessageId = await channelRuntime.sendScheduledMessage(job.workspaceId, job.route, execution.replyText);
35
+ if (!platformMessageId) {
36
+ throw new Error('Lark gateway did not return a message id for scheduled delivery.');
37
+ }
38
+ }
39
+ return {
40
+ threadId: execution.threadId,
41
+ runId: execution.runId,
42
+ replyText: execution.replyText,
43
+ platformMessageId: platformMessageId || undefined,
44
+ };
45
+ }
46
+ async resolveThread(job) {
47
+ const workspaceRouter = this.options.getWorkspaceRouter();
48
+ const route = job.route;
49
+ if (route.threadId) {
50
+ await workspaceRouter.getThread(route.threadId);
51
+ return route.threadId;
52
+ }
53
+ const channelId = route.channelId;
54
+ const participantId = route.participantId || '';
55
+ const binding = this.options.store.getPlatformThreadBinding(job.workspaceId, channelId, participantId);
56
+ if (binding?.thread_id) {
57
+ return binding.thread_id;
58
+ }
59
+ const thread = await workspaceRouter.createThread(job.workspaceId, job.description || `Scheduled ${job.platform} task`);
60
+ const now = new Date().toISOString();
61
+ this.options.store.upsertPlatformThreadBinding({
62
+ workspace_id: job.workspaceId,
63
+ chat_id: channelId,
64
+ platform_user_id: participantId,
65
+ thread_id: thread.id,
66
+ last_platform_message_id: null,
67
+ created_at: now,
68
+ updated_at: now,
69
+ });
70
+ const authorized = this.options.store.getAuthorizedUser(job.workspaceId, participantId);
71
+ if (authorized) {
72
+ this.options.store.updateAuthorizedUserThread(job.workspaceId, participantId, thread.id);
73
+ }
74
+ return thread.id;
75
+ }
76
+ }
77
+ exports.LarkScheduleAdapter = LarkScheduleAdapter;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScheduledConversationExecutor = void 0;
4
+ const TERMINAL_RUN_STATES = new Set(['completed', 'failed', 'interrupted']);
5
+ class ScheduledConversationExecutor {
6
+ options;
7
+ constructor(options) {
8
+ this.options = options;
9
+ }
10
+ async execute(job, prompt, policy, timeoutMs = 15 * 60 * 1000) {
11
+ const target = await policy.resolveTarget(job);
12
+ await policy.beforeExecute?.(target, job);
13
+ try {
14
+ const sendResult = await this.options.workspaceRouter.sendThreadMessage(target.threadId, prompt);
15
+ await this.waitForRun(sendResult.runId, timeoutMs);
16
+ const thread = await this.options.workspaceRouter.getThread(target.threadId);
17
+ const replyText = [...thread.messages]
18
+ .reverse()
19
+ .find((message) => message.role === 'assistant' && message.kind === 'final')
20
+ ?.content;
21
+ return {
22
+ threadId: target.threadId,
23
+ runId: sendResult.runId,
24
+ replyText,
25
+ };
26
+ }
27
+ finally {
28
+ await policy.afterExecute?.(target, job);
29
+ }
30
+ }
31
+ async waitForRun(runId, timeoutMs) {
32
+ const startedAt = Date.now();
33
+ while (Date.now() - startedAt < timeoutMs) {
34
+ const run = this.options.store.getRun(runId);
35
+ if (run && TERMINAL_RUN_STATES.has(run.status)) {
36
+ if (run.status !== 'completed') {
37
+ throw new Error(`Scheduled run finished with status ${run.status}`);
38
+ }
39
+ return run;
40
+ }
41
+ await new Promise((resolve) => setTimeout(resolve, 300));
42
+ }
43
+ throw new Error(`Timed out waiting for scheduled run ${runId}`);
44
+ }
45
+ }
46
+ exports.ScheduledConversationExecutor = ScheduledConversationExecutor;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchedulerRunLifecycle = void 0;
4
+ class SchedulerRunLifecycle {
5
+ options;
6
+ constructor(options) {
7
+ this.options = options;
8
+ }
9
+ markSkipped(job, triggeredAt, error) {
10
+ const skipped = this.options.store.createScheduledJobRun(job.id, 'skipped', {
11
+ triggeredAt,
12
+ error,
13
+ });
14
+ this.options.emitRun(skipped);
15
+ this.emitCurrentJob(job.id);
16
+ return skipped;
17
+ }
18
+ markQueued(job, triggeredAt) {
19
+ const run = this.options.store.createScheduledJobRun(job.id, 'queued', { triggeredAt });
20
+ this.options.emitRun(run);
21
+ return run;
22
+ }
23
+ markRunning(runId) {
24
+ const started = this.options.store.updateScheduledJobRun(runId, {
25
+ status: 'running',
26
+ startedAt: new Date().toISOString(),
27
+ });
28
+ this.options.emitRun(started);
29
+ return started;
30
+ }
31
+ markSucceeded(job, runId, result, disableOnceJob) {
32
+ const completed = this.options.store.updateScheduledJobRun(runId, {
33
+ status: 'succeeded',
34
+ finishedAt: new Date().toISOString(),
35
+ threadId: result.threadId,
36
+ runId: result.runId,
37
+ platformMessageId: result.platformMessageId,
38
+ error: '',
39
+ });
40
+ if (disableOnceJob) {
41
+ this.options.store.updateScheduledJobStatus(job.id, { enabled: false });
42
+ }
43
+ this.options.emitRun(completed);
44
+ this.emitCurrentJob(job.id);
45
+ return completed;
46
+ }
47
+ markFailed(jobId, runId, error) {
48
+ const failed = this.options.store.updateScheduledJobRun(runId, {
49
+ status: 'failed',
50
+ finishedAt: new Date().toISOString(),
51
+ error,
52
+ });
53
+ this.options.emitRun(failed);
54
+ this.emitCurrentJob(jobId);
55
+ return failed;
56
+ }
57
+ emitCurrentJob(jobId) {
58
+ const job = this.options.store.getScheduledJob(jobId);
59
+ if (job) {
60
+ this.options.emitJob(job);
61
+ }
62
+ }
63
+ }
64
+ exports.SchedulerRunLifecycle = SchedulerRunLifecycle;
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchedulerService = void 0;
4
+ const node_events_1 = require("node:events");
5
+ const scheduler_run_lifecycle_js_1 = require("./scheduler-run-lifecycle.js");
6
+ class SchedulerService extends node_events_1.EventEmitter {
7
+ options;
8
+ timer = null;
9
+ pollInFlight = false;
10
+ runningJobs = new Set();
11
+ runLifecycle;
12
+ constructor(options) {
13
+ super();
14
+ this.options = options;
15
+ this.runLifecycle = new scheduler_run_lifecycle_js_1.SchedulerRunLifecycle({
16
+ store: options.store,
17
+ emitRun: (run) => this.emit('run', run),
18
+ emitJob: (job) => this.emit('job', job),
19
+ });
20
+ }
21
+ async start() {
22
+ await this.tick();
23
+ this.timer = setInterval(() => {
24
+ void this.tick();
25
+ }, 1000);
26
+ }
27
+ async stop() {
28
+ if (this.timer) {
29
+ clearInterval(this.timer);
30
+ this.timer = null;
31
+ }
32
+ }
33
+ listJobs(workspaceId) {
34
+ return this.options.store.listScheduledJobs(workspaceId);
35
+ }
36
+ getJob(jobId) {
37
+ return this.options.store.getScheduledJob(jobId);
38
+ }
39
+ createJob(input) {
40
+ const job = this.options.store.createScheduledJob(input);
41
+ this.emit('job', job);
42
+ this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: job });
43
+ return job;
44
+ }
45
+ updateJob(jobId, input) {
46
+ const job = this.options.store.updateScheduledJob(jobId, input);
47
+ this.emit('job', job);
48
+ this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: job });
49
+ return job;
50
+ }
51
+ deleteJob(jobId) {
52
+ return this.options.store.deleteScheduledJob(jobId);
53
+ }
54
+ listJobRuns(jobId) {
55
+ return this.options.store.listScheduledJobRuns(jobId);
56
+ }
57
+ async runJobNow(jobId) {
58
+ const job = this.options.store.getScheduledJob(jobId);
59
+ if (!job) {
60
+ throw new Error(`Scheduled job not found: ${jobId}`);
61
+ }
62
+ return this.executeJob(job, new Date().toISOString(), true);
63
+ }
64
+ async tick() {
65
+ if (this.pollInFlight) {
66
+ return;
67
+ }
68
+ this.pollInFlight = true;
69
+ try {
70
+ const now = new Date();
71
+ const jobs = this.options.store.listScheduledJobs().filter((job) => job.enabled);
72
+ for (const job of jobs) {
73
+ if (!this.isDue(job, now)) {
74
+ continue;
75
+ }
76
+ await this.executeJob(job, now.toISOString(), false);
77
+ }
78
+ }
79
+ finally {
80
+ this.pollInFlight = false;
81
+ }
82
+ }
83
+ isDue(job, now) {
84
+ const trigger = this.options.triggers.find((candidate) => candidate.supports(job));
85
+ if (!trigger) {
86
+ return false;
87
+ }
88
+ return trigger.isDue(job, now);
89
+ }
90
+ async executeJob(job, triggeredAt, manual) {
91
+ if (this.runningJobs.has(job.id)) {
92
+ const skipped = this.runLifecycle.markSkipped(job, triggeredAt, 'Skipped because the previous run is still active.');
93
+ this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: skipped });
94
+ return skipped;
95
+ }
96
+ this.runningJobs.add(job.id);
97
+ const run = this.runLifecycle.markQueued(job, triggeredAt);
98
+ this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: run });
99
+ try {
100
+ const executor = this.options.executors.find((candidate) => candidate.supports(job));
101
+ if (!executor) {
102
+ throw new Error(`No scheduler executor is available for delivery target "${job.platform}"`);
103
+ }
104
+ this.runLifecycle.markRunning(run.id);
105
+ const running = this.options.store.getScheduledJobRun(run.id);
106
+ if (running) {
107
+ this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: running });
108
+ }
109
+ const result = await executor.execute({ job, triggeredAt });
110
+ const succeeded = this.runLifecycle.markSucceeded(job, run.id, result, !manual && job.triggerType === 'once');
111
+ this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: succeeded });
112
+ const nextJob = this.options.store.getScheduledJob(job.id);
113
+ if (nextJob) {
114
+ this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: nextJob });
115
+ }
116
+ return succeeded;
117
+ }
118
+ catch (error) {
119
+ const failed = this.runLifecycle.markFailed(job.id, run.id, error instanceof Error ? error.message : String(error));
120
+ this.options.eventBus.emit({ type: 'scheduler.run.updated', payload: failed });
121
+ const nextJob = this.options.store.getScheduledJob(job.id);
122
+ if (nextJob) {
123
+ this.options.eventBus.emit({ type: 'scheduler.job.updated', payload: nextJob });
124
+ }
125
+ this.options.log?.(`scheduler job failed ${job.id}: ${failed.error || 'unknown error'}`);
126
+ return failed;
127
+ }
128
+ finally {
129
+ this.runningJobs.delete(job.id);
130
+ }
131
+ }
132
+ }
133
+ exports.SchedulerService = SchedulerService;
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWeixinExecutionPolicy = createWeixinExecutionPolicy;
4
+ function createWeixinExecutionPolicy(job, options, resolveSameThread) {
5
+ if (job.executionMode === 'side-thread') {
6
+ return new WeixinSideThreadExecutionPolicy(options);
7
+ }
8
+ return new WeixinSameThreadExecutionPolicy(options, resolveSameThread);
9
+ }
10
+ class WeixinSameThreadExecutionPolicy {
11
+ options;
12
+ resolveSameThread;
13
+ constructor(options, resolveSameThread) {
14
+ this.options = options;
15
+ this.resolveSameThread = resolveSameThread;
16
+ }
17
+ async resolveTarget(job) {
18
+ return {
19
+ kind: 'thread',
20
+ threadId: await this.resolveSameThread(job),
21
+ workspaceId: job.workspaceId,
22
+ platform: job.platform,
23
+ route: job.route,
24
+ };
25
+ }
26
+ beforeExecute(target) {
27
+ this.options.getChannelRuntime().muteThreadBridge?.(target.threadId);
28
+ }
29
+ afterExecute(target) {
30
+ this.options.getChannelRuntime().unmuteThreadBridge?.(target.threadId);
31
+ }
32
+ }
33
+ class WeixinSideThreadExecutionPolicy {
34
+ options;
35
+ constructor(options) {
36
+ this.options = options;
37
+ }
38
+ async resolveTarget(job) {
39
+ const title = `[Scheduled] ${job.description || job.id}`;
40
+ const existing = (await this.options.workspaceRouter.listThreads(job.workspaceId))
41
+ .find((thread) => thread.title === title);
42
+ if (existing) {
43
+ return {
44
+ kind: 'thread',
45
+ threadId: existing.id,
46
+ workspaceId: job.workspaceId,
47
+ platform: job.platform,
48
+ route: job.route,
49
+ };
50
+ }
51
+ const created = await this.options.workspaceRouter.createThread(job.workspaceId, title);
52
+ return {
53
+ kind: 'thread',
54
+ threadId: created.id,
55
+ workspaceId: job.workspaceId,
56
+ platform: job.platform,
57
+ route: job.route,
58
+ };
59
+ }
60
+ beforeExecute(target) {
61
+ this.options.getChannelRuntime().muteThreadBridge?.(target.threadId);
62
+ }
63
+ afterExecute(target) {
64
+ this.options.getChannelRuntime().unmuteThreadBridge?.(target.threadId);
65
+ }
66
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WeixinScheduleAdapter = void 0;
4
+ const scheduled_conversation_executor_js_1 = require("./scheduled-conversation-executor.js");
5
+ const weixin_execution_policies_js_1 = require("./weixin-execution-policies.js");
6
+ class WeixinScheduleAdapter {
7
+ options;
8
+ executor;
9
+ deliveryTargets = ['weixin'];
10
+ constructor(options) {
11
+ this.options = options;
12
+ this.executor = new scheduled_conversation_executor_js_1.ScheduledConversationExecutor({
13
+ store: options.store,
14
+ workspaceRouter: options.getWorkspaceRouter(),
15
+ });
16
+ }
17
+ supports(job) {
18
+ return job.platform === 'weixin' && (job.route.type === 'channel.chat' || job.route.type === 'weixin_chat');
19
+ }
20
+ async execute(context) {
21
+ const { job } = context;
22
+ const executionPolicy = (0, weixin_execution_policies_js_1.createWeixinExecutionPolicy)(job, {
23
+ store: this.options.store,
24
+ workspaceRouter: this.options.getWorkspaceRouter(),
25
+ getChannelRuntime: this.options.getChannelRuntime,
26
+ }, (nextJob) => this.resolveThread(nextJob));
27
+ const execution = await this.executor.execute(job, job.promptTemplate, executionPolicy);
28
+ let platformMessageId = '';
29
+ if (execution.replyText) {
30
+ const channelRuntime = this.options.getChannelRuntime();
31
+ if (!channelRuntime.sendScheduledMessage) {
32
+ throw new Error('WeChat channel runtime does not support scheduled delivery.');
33
+ }
34
+ platformMessageId = await channelRuntime.sendScheduledMessage(job.workspaceId, job.route, execution.replyText);
35
+ if (!platformMessageId) {
36
+ throw new Error('WeChat gateway did not return a message id for scheduled delivery.');
37
+ }
38
+ }
39
+ return {
40
+ threadId: execution.threadId,
41
+ runId: execution.runId,
42
+ replyText: execution.replyText,
43
+ platformMessageId: platformMessageId || undefined,
44
+ };
45
+ }
46
+ async resolveThread(job) {
47
+ const workspaceRouter = this.options.getWorkspaceRouter();
48
+ const route = job.route;
49
+ if (route.threadId) {
50
+ await workspaceRouter.getThread(route.threadId);
51
+ return route.threadId;
52
+ }
53
+ const channelId = route.channelId;
54
+ const participantId = route.participantId || '';
55
+ const binding = this.options.store.getPlatformThreadBinding(job.workspaceId, channelId, participantId, 'weixin');
56
+ if (binding?.thread_id) {
57
+ return binding.thread_id;
58
+ }
59
+ const thread = await workspaceRouter.createThread(job.workspaceId, job.description || `Scheduled ${job.platform} task`);
60
+ const now = new Date().toISOString();
61
+ this.options.store.upsertPlatformThreadBinding({
62
+ workspace_id: job.workspaceId,
63
+ platform: 'weixin',
64
+ chat_id: channelId,
65
+ platform_user_id: participantId,
66
+ thread_id: thread.id,
67
+ last_platform_message_id: null,
68
+ created_at: now,
69
+ updated_at: now,
70
+ });
71
+ const authorized = this.options.store.getAuthorizedUser(job.workspaceId, participantId, 'weixin');
72
+ if (authorized) {
73
+ this.options.store.updateAuthorizedUserThread(job.workspaceId, participantId, thread.id, 'weixin');
74
+ }
75
+ return thread.id;
76
+ }
77
+ }
78
+ exports.WeixinScheduleAdapter = WeixinScheduleAdapter;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.encodeThreadId = encodeThreadId;
4
+ exports.decodeThreadId = decodeThreadId;
5
+ function encodeThreadId(workspaceId, sessionId) {
6
+ return `${encodeURIComponent(workspaceId)}::${encodeURIComponent(sessionId)}`;
7
+ }
8
+ function decodeThreadId(threadId) {
9
+ const [workspacePart, sessionPart] = threadId.split('::');
10
+ if (!workspacePart || !sessionPart) {
11
+ throw new Error(`Invalid thread id: ${threadId}`);
12
+ }
13
+ return {
14
+ workspaceId: decodeURIComponent(workspacePart),
15
+ sessionId: decodeURIComponent(sessionPart),
16
+ };
17
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeMessageContent = normalizeMessageContent;
4
+ exports.toThreadMessages = toThreadMessages;
5
+ exports.toThreadSummary = toThreadSummary;
6
+ exports.toThreadDetail = toThreadDetail;
7
+ const workspace_thread_id_js_1 = require("./workspace-thread-id.js");
8
+ function normalizeMessageContent(content) {
9
+ return String(content || '').replace(/\n/g, ' ').trim();
10
+ }
11
+ function toThreadMessages(history) {
12
+ return history.map((message, index) => ({
13
+ id: `${message.timestamp || index}-${message.role}-${index}`,
14
+ role: message.role === 'user' ? 'user' : message.role === 'assistant' ? 'assistant' : 'system',
15
+ content: message.content,
16
+ timestamp: message.timestamp,
17
+ kind: message.kind === 'progress' ? 'progress' : message.role === 'system' ? 'system' : 'final',
18
+ }));
19
+ }
20
+ function toThreadSummary(workspaceId, session) {
21
+ const id = (0, workspace_thread_id_js_1.encodeThreadId)(workspaceId, session.id);
22
+ return {
23
+ id,
24
+ workspaceId,
25
+ title: String(session.name || session.user_name || session.chat_name || session.id).trim(),
26
+ live: Boolean(session.live || session.active),
27
+ updatedAt: session.updated_at,
28
+ createdAt: session.created_at,
29
+ historyCount: session.history_count,
30
+ excerpt: normalizeMessageContent(session.last_message?.content),
31
+ participantName: session.user_name || session.chat_name,
32
+ runId: session.live ? `run:${id}` : undefined,
33
+ bridgeSessionKey: session.session_key,
34
+ agentType: session.agent_type,
35
+ };
36
+ }
37
+ function toThreadDetail(workspaceId, session, selectedKnowledgeBaseIds = []) {
38
+ return {
39
+ ...toThreadSummary(workspaceId, session),
40
+ messages: toThreadMessages(session.history || []),
41
+ selectedKnowledgeBaseIds,
42
+ };
43
+ }