@pellux/goodvibes-tui 0.24.1 → 0.26.0

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 (63) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/docs/foundation-artifacts/operator-contract.json +2419 -1040
  4. package/package.json +2 -2
  5. package/src/daemon/handlers/calendar/caldav-client.ts +661 -0
  6. package/src/daemon/handlers/calendar/ics.ts +556 -0
  7. package/src/daemon/handlers/calendar/index.ts +316 -0
  8. package/src/daemon/handlers/context.ts +29 -0
  9. package/src/daemon/handlers/contracts.ts +77 -0
  10. package/src/daemon/handlers/credentials.ts +129 -0
  11. package/src/daemon/handlers/drafts/draft-store.ts +427 -0
  12. package/src/daemon/handlers/drafts/index.ts +17 -0
  13. package/src/daemon/handlers/drafts/register.ts +331 -0
  14. package/src/daemon/handlers/email/config.ts +164 -0
  15. package/src/daemon/handlers/email/imap-connector.ts +441 -0
  16. package/src/daemon/handlers/email/imap-parsing.ts +499 -0
  17. package/src/daemon/handlers/email/index.ts +43 -0
  18. package/src/daemon/handlers/email/read-handlers.ts +80 -0
  19. package/src/daemon/handlers/email/runtime.ts +140 -0
  20. package/src/daemon/handlers/email/smtp-connector.ts +557 -0
  21. package/src/daemon/handlers/email/validation.ts +147 -0
  22. package/src/daemon/handlers/email/write-handlers.ts +133 -0
  23. package/src/daemon/handlers/errors.ts +18 -0
  24. package/src/daemon/handlers/inbox/cursor-store.ts +290 -0
  25. package/src/daemon/handlers/inbox/index.ts +210 -0
  26. package/src/daemon/handlers/inbox/mapping.ts +192 -0
  27. package/src/daemon/handlers/inbox/poller.ts +155 -0
  28. package/src/daemon/handlers/inbox/provider-adapter.ts +156 -0
  29. package/src/daemon/handlers/inbox/providers/discord.ts +251 -0
  30. package/src/daemon/handlers/inbox/providers/email.ts +151 -0
  31. package/src/daemon/handlers/inbox/providers/imap-client.ts +300 -0
  32. package/src/daemon/handlers/inbox/providers/route-util.ts +23 -0
  33. package/src/daemon/handlers/inbox/providers/slack.ts +262 -0
  34. package/src/daemon/handlers/index.ts +107 -0
  35. package/src/daemon/handlers/register.ts +161 -0
  36. package/src/daemon/handlers/remote/backends/cloud-terminal.ts +142 -0
  37. package/src/daemon/handlers/remote/backends/docker.ts +79 -0
  38. package/src/daemon/handlers/remote/backends/index.ts +40 -0
  39. package/src/daemon/handlers/remote/backends/local-process.ts +113 -0
  40. package/src/daemon/handlers/remote/backends/process-runner.ts +127 -0
  41. package/src/daemon/handlers/remote/backends/ssh.ts +125 -0
  42. package/src/daemon/handlers/remote/backends/types.ts +97 -0
  43. package/src/daemon/handlers/remote/dispatcher.ts +181 -0
  44. package/src/daemon/handlers/remote/index.ts +119 -0
  45. package/src/daemon/handlers/remote/peer-registry.ts +357 -0
  46. package/src/daemon/handlers/remote/service.ts +191 -0
  47. package/src/daemon/handlers/routing/inbox-bridge.ts +71 -0
  48. package/src/daemon/handlers/routing/index.ts +261 -0
  49. package/src/daemon/handlers/routing/route-store.ts +319 -0
  50. package/src/daemon/handlers/routing/routing-resolver.ts +75 -0
  51. package/src/daemon/handlers/sqlite-store.ts +124 -0
  52. package/src/daemon/handlers/triage/index.ts +57 -0
  53. package/src/daemon/handlers/triage/integration.ts +212 -0
  54. package/src/daemon/handlers/triage/pipeline.ts +273 -0
  55. package/src/daemon/handlers/triage/scorer.ts +287 -0
  56. package/src/daemon/handlers/triage/tagger/discord.ts +186 -0
  57. package/src/daemon/handlers/triage/tagger/imap.ts +383 -0
  58. package/src/daemon/handlers/triage/tagger/index.ts +184 -0
  59. package/src/daemon/handlers/triage/tagger/shared.ts +70 -0
  60. package/src/daemon/handlers/triage/tagger/slack.ts +69 -0
  61. package/src/daemon/handlers/triage/types.ts +50 -0
  62. package/src/runtime/services.ts +48 -0
  63. package/src/version.ts +1 -1
@@ -0,0 +1,69 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Triage tagger — Slack provider.
3
+ //
4
+ // Applies a triage label as a Slack message reaction (reactions.add). The bot
5
+ // token is resolved per-apply from the daemon credential store and is never
6
+ // logged or returned. `already_reacted` is treated as idempotent success.
7
+ // ---------------------------------------------------------------------------
8
+
9
+ import type { HandlerContext } from '../../context.ts';
10
+ import type { DaemonCredentialStore } from '../../credentials.ts';
11
+ import { HandlerError } from '../../errors.ts';
12
+ import type { InboundChannelItem } from '../types.ts';
13
+ import type { ApplyTagsResult, TaggerProviderConfig } from './shared.ts';
14
+ import { slackEmojiForTag, stringMeta } from './shared.ts';
15
+
16
+ export async function applySlack(
17
+ item: InboundChannelItem,
18
+ tags: string[],
19
+ providers: TaggerProviderConfig,
20
+ credentials: DaemonCredentialStore,
21
+ fetchImpl: typeof fetch,
22
+ ctx: HandlerContext,
23
+ base: ApplyTagsResult,
24
+ ): Promise<ApplyTagsResult> {
25
+ const cfg = providers.slack;
26
+ if (!cfg) return { ...base, reason: 'slack-not-configured' };
27
+ const channel = stringMeta(item, 'channelId') ?? item.conversationId;
28
+ const ts = stringMeta(item, 'ts') ?? stringMeta(item, 'messageTs');
29
+ if (!channel || !ts) return { ...base, reason: 'slack-missing-target' };
30
+ if (tags.length === 0) return { ...base, reason: 'no-tags' };
31
+
32
+ const token = await credentials.resolveConfigSecret(cfg.tokenConfigKey);
33
+ if (!token) return { ...base, reason: 'slack-no-credentials' };
34
+
35
+ const applied: string[] = [];
36
+ for (const tag of tags) {
37
+ const emoji = slackEmojiForTag(tag);
38
+ const response = await fetchImpl('https://slack.com/api/reactions.add', {
39
+ method: 'POST',
40
+ headers: {
41
+ Authorization: `Bearer ${token}`,
42
+ 'Content-Type': 'application/json; charset=utf-8',
43
+ },
44
+ body: JSON.stringify({ channel, timestamp: ts, name: emoji }),
45
+ });
46
+ const payload = (await response.json().catch(() => ({}))) as {
47
+ ok?: boolean;
48
+ error?: string;
49
+ };
50
+ if (!response.ok) {
51
+ throw new HandlerError(
52
+ `Slack reactions.add HTTP ${response.status}`,
53
+ 'TRIAGE_SLACK_TAG_FAILED',
54
+ 502,
55
+ );
56
+ }
57
+ // 'already_reacted' is an idempotent success for our purposes.
58
+ if (payload.ok !== true && payload.error !== 'already_reacted') {
59
+ ctx.logger.warn('triage: slack reaction rejected', { error: payload.error });
60
+ throw new HandlerError(
61
+ `Slack reactions.add rejected: ${payload.error ?? 'unknown'}`,
62
+ 'TRIAGE_SLACK_TAG_FAILED',
63
+ 502,
64
+ );
65
+ }
66
+ applied.push(tag);
67
+ }
68
+ return { surface: item.surface, itemId: item.id, appliedTags: applied, skipped: false };
69
+ }
@@ -0,0 +1,50 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Daemon-internal triage domain types.
3
+ //
4
+ // `InboundChannelItem` is the daemon-internal shape the inbox poller produces
5
+ // and the triage pipeline scores. It is NOT an SDK catalog contract: the
6
+ // published `channels.inbox.list` output schema (CHANNEL_INBOX_ITEM_SCHEMA) is
7
+ // owned by the SDK and never re-declared here. This is the internal poller
8
+ // item per the handoff doc — it carries `fromDigest` (never a raw sender id),
9
+ // `subjectPreview`/`bodyPreview` (PII-stripped, length-bounded) and an opaque
10
+ // `metadata` bag the tagger reads provider targeting from (imapUid, channelId,
11
+ // Slack ts, Discord messageId/threadId).
12
+ // ---------------------------------------------------------------------------
13
+
14
+ /** Triage label assigned by the scorer. */
15
+ export type TriageLabel = 'spam' | 'priority' | 'normal';
16
+
17
+ /** 1:1 vs group/channel/thread conversation hint (priority signal). */
18
+ export type ConversationKind = 'direct' | 'group' | 'channel' | 'thread' | 'service';
19
+
20
+ /**
21
+ * Internal inbound feed item. Mirrors the handoff `InboundChannelItem` shape
22
+ * plus the optional fields the scorer/tagger consult. `surface` is the provider
23
+ * family ('email' | 'imap' | 'slack' | 'discord' | ...) the tagger dispatches
24
+ * on; `provider` is the handoff-facing provider id. They are usually equal.
25
+ */
26
+ export interface InboundChannelItem {
27
+ /** Stable, provider-scoped dedup key. */
28
+ readonly id: string;
29
+ /** Provider family the tagger dispatches on (email/imap/slack/discord/...). */
30
+ readonly surface: string;
31
+ /** Handoff-facing provider id ("slack" | "discord" | "email" | ...). */
32
+ readonly provider?: string;
33
+ readonly kind?: 'dm' | 'thread' | 'mention' | 'reaction';
34
+ /** SHA-256 first-N of sender external id — NEVER a raw identifier. */
35
+ readonly fromDigest?: string;
36
+ /** Conversation id (Slack/Discord channel, IMAP mailbox-scoped). */
37
+ readonly conversationId?: string;
38
+ readonly conversationKind?: ConversationKind;
39
+ /** Display subject (<= 200 chars). */
40
+ readonly subject?: string;
41
+ /** Display body preview (<= 500 chars, PII-stripped). Alias: bodyPreview. */
42
+ readonly snippet?: string;
43
+ /** Optional daemon route binding id. */
44
+ readonly routeId?: string;
45
+ /** Unix ms. */
46
+ readonly receivedAt?: number;
47
+ readonly unread?: boolean;
48
+ /** Opaque provider targeting bag (imapUid/uid, channelId, ts, messageId, threadId). */
49
+ readonly metadata?: Record<string, unknown>;
50
+ }
@@ -87,6 +87,19 @@ import {
87
87
  type WorkflowServices,
88
88
  } from '@pellux/goodvibes-sdk/platform/tools';
89
89
  import { WorkPlanStore } from '../work-plans/work-plan-store.ts';
90
+ import {
91
+ registerDaemonHandlers,
92
+ type DaemonHandlerSurfaces,
93
+ } from '../daemon/handlers/index.ts';
94
+ import type { HandlerContext, HandlerLogger } from '../daemon/handlers/context.ts';
95
+ import { createDaemonCredentialStore } from '../daemon/handlers/credentials.ts';
96
+ import { registerRouting } from '../daemon/handlers/routing/index.ts';
97
+ import { registerInboxMethods } from '../daemon/handlers/inbox/index.ts';
98
+ import { registerTriagedInbox } from '../daemon/handlers/triage/index.ts';
99
+ import { registerDraftMethods } from '../daemon/handlers/drafts/index.ts';
100
+ import { registerCalendar } from '../daemon/handlers/calendar/index.ts';
101
+ import { registerEmailMethods } from '../daemon/handlers/email/index.ts';
102
+ import { registerRemoteSurface } from '../daemon/handlers/remote/index.ts';
90
103
 
91
104
  const REGULAR_KNOWLEDGE_DB_FILE = 'knowledge-wiki.sqlite';
92
105
  const HOME_GRAPH_KNOWLEDGE_DB_FILE = 'knowledge-home-graph.sqlite';
@@ -215,6 +228,7 @@ export interface RuntimeServices {
215
228
  readonly providerRegistry: ProviderRegistry;
216
229
  readonly toolLLM: ToolLLM;
217
230
  readonly distributedRuntime: DistributedRuntimeManager;
231
+ readonly daemonHandlers: DaemonHandlerSurfaces;
218
232
  readonly remoteRunnerRegistry: RemoteRunnerRegistry;
219
233
  readonly remoteSupervisor: RemoteSupervisor;
220
234
  readonly sessionMemoryStore: SessionMemoryStore;
@@ -517,6 +531,39 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
517
531
  approvalBridge: approvalBroker,
518
532
  automationBridge: automationManager,
519
533
  });
534
+
535
+ // Daemon handler surfaces: attach HOST handlers to the SDK-auto-registered
536
+ // builtin gateway descriptors (channels.* / email.* / calendar.*). The SDK
537
+ // owns every id/descriptor/schema; this only fills the handler slot via
538
+ // catalog.register(descriptor, handler, { replace: true }). Routing returns
539
+ // the resolver the inbox surface consumes; triage decorates channels.inbox.list.
540
+ // The remote surface reuses the SAME DistributedRuntimeManager the SDK facade
541
+ // injects, so its peer/work methods share one persistent store; remote.peers.*
542
+ // stay SDK-published routes (not catalog methods).
543
+ const handlerLogger: HandlerLogger = {
544
+ info: (message, meta) => console.info(message, meta ?? ''),
545
+ warn: (message, meta) => console.warn(message, meta ?? ''),
546
+ error: (message, meta) => console.error(message, meta ?? ''),
547
+ };
548
+ const handlerContext: HandlerContext = {
549
+ catalog: gatewayMethods,
550
+ credentials: createDaemonCredentialStore(secretsManager),
551
+ configManager,
552
+ workingDirectory,
553
+ homeDirectory,
554
+ logger: handlerLogger,
555
+ };
556
+ const daemonHandlers = registerDaemonHandlers(handlerContext, {
557
+ registerRouting,
558
+ registerInbox: (ctx, routing) =>
559
+ registerTriagedInbox(ctx, (inboxCtx) => registerInboxMethods(inboxCtx, routing))
560
+ .unregister,
561
+ registerDrafts: (ctx) => registerDraftMethods(ctx),
562
+ registerCalendar,
563
+ registerEmail: (ctx) => registerEmailMethods(ctx),
564
+ registerRemote: (ctx) => registerRemoteSurface(ctx, { manager: distributedRuntime }),
565
+ });
566
+
520
567
  const remoteRunnerRegistry = new RemoteRunnerRegistry(agentManager);
521
568
  const remoteSupervisor = new RemoteSupervisor(remoteRunnerRegistry);
522
569
  const sandboxSessionRegistry = new SandboxSessionRegistry(workingDirectory);
@@ -669,6 +716,7 @@ export function createRuntimeServices(options: RuntimeServicesOptions): RuntimeS
669
716
  providerRegistry,
670
717
  toolLLM,
671
718
  distributedRuntime,
719
+ daemonHandlers,
672
720
  remoteRunnerRegistry,
673
721
  remoteSupervisor,
674
722
  sessionMemoryStore,
package/src/version.ts CHANGED
@@ -6,7 +6,7 @@ import { join } from 'node:path';
6
6
  // The prebuild script updates the fallback value before compilation.
7
7
  // Uses import.meta.dir (Bun) to locate package.json relative to this file,
8
8
  // which is correct regardless of the process working directory.
9
- let _version = '0.24.1';
9
+ let _version = '0.26.0';
10
10
  try {
11
11
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
12
12
  _version = pkg.version ?? _version;