jinn-cli 0.1.1 → 0.2.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 (87) hide show
  1. package/dist/bin/jimmy.js +1 -1
  2. package/dist/src/connectors/cron/index.d.ts +21 -0
  3. package/dist/src/connectors/cron/index.d.ts.map +1 -0
  4. package/dist/src/connectors/cron/index.js +75 -0
  5. package/dist/src/connectors/cron/index.js.map +1 -0
  6. package/dist/src/connectors/slack/index.d.ts +13 -5
  7. package/dist/src/connectors/slack/index.d.ts.map +1 -1
  8. package/dist/src/connectors/slack/index.js +80 -3
  9. package/dist/src/connectors/slack/index.js.map +1 -1
  10. package/dist/src/connectors/slack/threads.d.ts +14 -1
  11. package/dist/src/connectors/slack/threads.d.ts.map +1 -1
  12. package/dist/src/connectors/slack/threads.js +23 -2
  13. package/dist/src/connectors/slack/threads.js.map +1 -1
  14. package/dist/src/connectors/slack/threads.test.d.ts +2 -0
  15. package/dist/src/connectors/slack/threads.test.d.ts.map +1 -0
  16. package/dist/src/connectors/slack/threads.test.js +45 -0
  17. package/dist/src/connectors/slack/threads.test.js.map +1 -0
  18. package/dist/src/cron/runner.d.ts +3 -2
  19. package/dist/src/cron/runner.d.ts.map +1 -1
  20. package/dist/src/cron/runner.js +48 -89
  21. package/dist/src/cron/runner.js.map +1 -1
  22. package/dist/src/cron/scheduler.d.ts +5 -2
  23. package/dist/src/cron/scheduler.d.ts.map +1 -1
  24. package/dist/src/cron/scheduler.js +29 -4
  25. package/dist/src/cron/scheduler.js.map +1 -1
  26. package/dist/src/engines/claude.js +1 -1
  27. package/dist/src/engines/claude.js.map +1 -1
  28. package/dist/src/gateway/api.d.ts.map +1 -1
  29. package/dist/src/gateway/api.js +58 -26
  30. package/dist/src/gateway/api.js.map +1 -1
  31. package/dist/src/gateway/server.d.ts.map +1 -1
  32. package/dist/src/gateway/server.js +5 -3
  33. package/dist/src/gateway/server.js.map +1 -1
  34. package/dist/src/sessions/manager.d.ts +12 -25
  35. package/dist/src/sessions/manager.d.ts.map +1 -1
  36. package/dist/src/sessions/manager.js +171 -96
  37. package/dist/src/sessions/manager.js.map +1 -1
  38. package/dist/src/sessions/queue.d.ts +5 -0
  39. package/dist/src/sessions/queue.d.ts.map +1 -1
  40. package/dist/src/sessions/queue.js +26 -0
  41. package/dist/src/sessions/queue.js.map +1 -1
  42. package/dist/src/sessions/queue.test.d.ts +2 -0
  43. package/dist/src/sessions/queue.test.d.ts.map +1 -0
  44. package/dist/src/sessions/queue.test.js +28 -0
  45. package/dist/src/sessions/queue.test.js.map +1 -0
  46. package/dist/src/sessions/registry.d.ts +12 -1
  47. package/dist/src/sessions/registry.d.ts.map +1 -1
  48. package/dist/src/sessions/registry.js +89 -11
  49. package/dist/src/sessions/registry.js.map +1 -1
  50. package/dist/src/sessions/registry.test.d.ts +2 -0
  51. package/dist/src/sessions/registry.test.d.ts.map +1 -0
  52. package/dist/src/sessions/registry.test.js +41 -0
  53. package/dist/src/sessions/registry.test.js.map +1 -0
  54. package/dist/src/shared/types.d.ts +43 -1
  55. package/dist/src/shared/types.d.ts.map +1 -1
  56. package/dist/web/404.html +1 -1
  57. package/dist/web/_next/static/chunks/app/sessions/page-c83c890177c18258.js +1 -0
  58. package/dist/web/_next/static/chunks/app/settings/page-2583d55551abdf72.js +1 -0
  59. package/dist/web/chat.html +1 -1
  60. package/dist/web/chat.txt +1 -1
  61. package/dist/web/costs.html +2 -2
  62. package/dist/web/costs.txt +1 -1
  63. package/dist/web/cron.html +1 -1
  64. package/dist/web/cron.txt +1 -1
  65. package/dist/web/index.html +1 -1
  66. package/dist/web/index.txt +1 -1
  67. package/dist/web/kanban.html +1 -1
  68. package/dist/web/kanban.txt +1 -1
  69. package/dist/web/logs.html +2 -2
  70. package/dist/web/logs.txt +1 -1
  71. package/dist/web/org.html +1 -1
  72. package/dist/web/org.txt +1 -1
  73. package/dist/web/sessions.html +1 -1
  74. package/dist/web/sessions.txt +2 -2
  75. package/dist/web/settings.html +1 -1
  76. package/dist/web/settings.txt +2 -2
  77. package/dist/web/skills.html +1 -1
  78. package/dist/web/skills.txt +1 -1
  79. package/package.json +9 -8
  80. package/template/config.default.yaml +5 -2
  81. package/template/migrations/0.2.0/MIGRATION.md +41 -0
  82. package/template/migrations/0.2.0/files/config.default.yaml +30 -0
  83. package/LICENSE +0 -21
  84. package/dist/web/_next/static/chunks/app/sessions/page-2eef6ac7882a28ba.js +0 -1
  85. package/dist/web/_next/static/chunks/app/settings/page-4fb01b9b09500170.js +0 -1
  86. /package/dist/web/_next/static/{vLvOwhC8JocJzSHTHKKOv → jt8R5JvUgfB9CMfFwWFr0}/_buildManifest.js +0 -0
  87. /package/dist/web/_next/static/{vLvOwhC8JocJzSHTHKKOv → jt8R5JvUgfB9CMfFwWFr0}/_ssgManifest.js +0 -0
package/dist/bin/jimmy.js CHANGED
@@ -6,7 +6,7 @@ const program = new Command();
6
6
  program
7
7
  .name("jinn")
8
8
  .description("Lightweight AI gateway daemon")
9
- .version("0.1.0")
9
+ .version("0.2.0")
10
10
  .option("-i, --instance <name>", "Target a specific instance (default: jinn)");
11
11
  // Pre-parse to set JINN_HOME before any module imports resolve paths
12
12
  program.hook("preAction", (thisCommand) => {
@@ -0,0 +1,21 @@
1
+ import type { Connector, ConnectorCapabilities, ConnectorHealth, CronDelivery, IncomingMessage, ReplyContext, Target } from "../../shared/types.js";
2
+ export declare class CronConnector implements Connector {
3
+ private readonly connectors;
4
+ private readonly delivery?;
5
+ name: string;
6
+ private handler;
7
+ constructor(connectors: Map<string, Connector>, delivery?: CronDelivery | undefined);
8
+ start(): Promise<void>;
9
+ stop(): Promise<void>;
10
+ getCapabilities(): ConnectorCapabilities;
11
+ getHealth(): ConnectorHealth;
12
+ reconstructTarget(replyContext: ReplyContext): Target;
13
+ sendMessage(target: Target, text: string): Promise<string | void>;
14
+ replyMessage(target: Target, text: string): Promise<string | void>;
15
+ addReaction(): Promise<void>;
16
+ removeReaction(): Promise<void>;
17
+ editMessage(target: Target, text: string): Promise<void>;
18
+ onMessage(handler: (msg: IncomingMessage) => void): void;
19
+ private forward;
20
+ }
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/connectors/cron/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,qBAAqB,EACrB,eAAe,EACf,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,MAAM,EACP,MAAM,uBAAuB,CAAC;AAU/B,qBAAa,aAAc,YAAW,SAAS;IAK3C,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAL5B,IAAI,SAAU;IACd,OAAO,CAAC,OAAO,CAAiD;gBAG7C,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EAClC,QAAQ,CAAC,EAAE,YAAY,YAAA;IAGpC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAEtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAE3B,eAAe,IAAI,qBAAqB;IAIxC,SAAS,IAAI,eAAe;IAO5B,iBAAiB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM;IAS/C,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIjE,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAIlE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAE5B,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAE/B,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9D,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,GAAG,IAAI;YAI1C,OAAO;CAqBtB"}
@@ -0,0 +1,75 @@
1
+ import { logger } from "../../shared/logger.js";
2
+ const capabilities = {
3
+ threading: false,
4
+ messageEdits: false,
5
+ reactions: false,
6
+ attachments: false,
7
+ };
8
+ export class CronConnector {
9
+ connectors;
10
+ delivery;
11
+ name = "cron";
12
+ handler = null;
13
+ constructor(connectors, delivery) {
14
+ this.connectors = connectors;
15
+ this.delivery = delivery;
16
+ }
17
+ async start() { }
18
+ async stop() { }
19
+ getCapabilities() {
20
+ return capabilities;
21
+ }
22
+ getHealth() {
23
+ return {
24
+ status: "running",
25
+ capabilities,
26
+ };
27
+ }
28
+ reconstructTarget(replyContext) {
29
+ return {
30
+ channel: typeof replyContext.channel === "string" ? replyContext.channel : "",
31
+ thread: typeof replyContext.thread === "string" ? replyContext.thread : undefined,
32
+ messageTs: typeof replyContext.messageTs === "string" ? replyContext.messageTs : undefined,
33
+ replyContext,
34
+ };
35
+ }
36
+ async sendMessage(target, text) {
37
+ return this.forward(target, text, false);
38
+ }
39
+ async replyMessage(target, text) {
40
+ return this.forward(target, text, true);
41
+ }
42
+ async addReaction() { }
43
+ async removeReaction() { }
44
+ async editMessage(target, text) {
45
+ if (!this.delivery)
46
+ return;
47
+ const connector = this.connectors.get(this.delivery.connector);
48
+ if (!connector || !connector.getCapabilities().messageEdits)
49
+ return;
50
+ await connector.editMessage(target, text);
51
+ }
52
+ onMessage(handler) {
53
+ this.handler = handler;
54
+ }
55
+ async forward(target, text, asReply) {
56
+ if (!this.delivery)
57
+ return undefined;
58
+ const connector = this.connectors.get(this.delivery.connector);
59
+ if (!connector) {
60
+ logger.warn(`Cron delivery connector "${this.delivery.connector}" not found`);
61
+ return undefined;
62
+ }
63
+ const resolvedTarget = {
64
+ channel: target.channel || this.delivery.channel,
65
+ thread: target.thread,
66
+ messageTs: target.messageTs,
67
+ replyContext: target.replyContext,
68
+ };
69
+ if (asReply) {
70
+ return connector.replyMessage(resolvedTarget, text);
71
+ }
72
+ return connector.sendMessage(resolvedTarget, text);
73
+ }
74
+ }
75
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/connectors/cron/index.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,23 @@
1
- import type { Connector, IncomingMessage, Target } from "../../shared/types.js";
1
+ import type { Connector, ConnectorCapabilities, ConnectorHealth, IncomingMessage, ReplyContext, SlackConnectorConfig, Target } from "../../shared/types.js";
2
2
  export declare class SlackConnector implements Connector {
3
3
  name: string;
4
4
  private app;
5
5
  private handler;
6
- constructor(config: {
7
- appToken: string;
8
- botToken: string;
9
- });
6
+ private readonly shareSessionInChannel;
7
+ private readonly allowedUsers;
8
+ private readonly ignoreOldMessagesOnBoot;
9
+ private readonly bootTimeMs;
10
+ private started;
11
+ private lastError;
12
+ private readonly capabilities;
13
+ constructor(config: SlackConnectorConfig);
10
14
  start(): Promise<void>;
11
15
  stop(): Promise<void>;
16
+ getCapabilities(): ConnectorCapabilities;
17
+ getHealth(): ConnectorHealth;
18
+ reconstructTarget(replyContext: ReplyContext): Target;
12
19
  sendMessage(target: Target, text: string): Promise<string | undefined>;
20
+ replyMessage(target: Target, text: string): Promise<string | undefined>;
13
21
  addReaction(target: Target, emoji: string): Promise<void>;
14
22
  removeReaction(target: Target, emoji: string): Promise<void>;
15
23
  editMessage(target: Target, text: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAMhF,qBAAa,cAAe,YAAW,SAAS;IAC9C,IAAI,SAAW;IACf,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,OAAO,CAAiD;gBAEpD,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE;IAQpD,KAAK;IAgDL,IAAI;IAKJ,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAgBtE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAazC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAa5C,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAU9C,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI;CAGlD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,SAAS,EACT,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,YAAY,EACZ,oBAAoB,EACpB,MAAM,EACP,MAAM,uBAAuB,CAAC;AAM/B,qBAAa,cAAe,YAAW,SAAS;IAC9C,IAAI,SAAW;IACf,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,OAAO,CAAiD;IAChE,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAU;IAChD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAClD,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAU;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAc;IACzC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAuB;IAExC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAK3B;gBAEU,MAAM,EAAE,oBAAoB;IAgBlC,KAAK;IAqEL,IAAI;IAMV,eAAe,IAAI,qBAAqB;IAIxC,SAAS,IAAI,eAAe;IAQ5B,iBAAiB,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM;IAS/C,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAetE,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAiBvE,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAazC,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAa5C,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAU9C,SAAS,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI;CAGlD"}
@@ -1,5 +1,5 @@
1
1
  import { App } from "@slack/bolt";
2
- import { deriveSourceRef } from "./threads.js";
2
+ import { buildReplyContext, deriveSessionKey, isOldSlackMessage } from "./threads.js";
3
3
  import { formatResponse, downloadAttachment } from "./format.js";
4
4
  import { TMP_DIR } from "../../shared/paths.js";
5
5
  import { logger } from "../../shared/logger.js";
@@ -7,12 +7,32 @@ export class SlackConnector {
7
7
  name = "slack";
8
8
  app;
9
9
  handler = null;
10
+ shareSessionInChannel;
11
+ allowedUsers;
12
+ ignoreOldMessagesOnBoot;
13
+ bootTimeMs = Date.now();
14
+ started = false;
15
+ lastError = null;
16
+ capabilities = {
17
+ threading: true,
18
+ messageEdits: true,
19
+ reactions: true,
20
+ attachments: true,
21
+ };
10
22
  constructor(config) {
11
23
  this.app = new App({
12
24
  token: config.botToken,
13
25
  appToken: config.appToken,
14
26
  socketMode: true,
15
27
  });
28
+ this.shareSessionInChannel = !!config.shareSessionInChannel;
29
+ this.ignoreOldMessagesOnBoot = config.ignoreOldMessagesOnBoot !== false;
30
+ const allowFrom = Array.isArray(config.allowFrom)
31
+ ? config.allowFrom
32
+ : typeof config.allowFrom === "string"
33
+ ? config.allowFrom.split(",").map((value) => value.trim()).filter(Boolean)
34
+ : [];
35
+ this.allowedUsers = allowFrom.length > 0 ? new Set(allowFrom) : null;
16
36
  }
17
37
  async start() {
18
38
  this.app.message(async ({ event }) => {
@@ -21,7 +41,18 @@ export class SlackConnector {
21
41
  return;
22
42
  if (!this.handler)
23
43
  return;
24
- const sourceRef = deriveSourceRef(event);
44
+ if (this.ignoreOldMessagesOnBoot && isOldSlackMessage(event.ts, this.bootTimeMs)) {
45
+ logger.debug(`Ignoring old Slack message ${event.ts}`);
46
+ return;
47
+ }
48
+ if (this.allowedUsers && !this.allowedUsers.has(event.user)) {
49
+ logger.debug(`Ignoring Slack message from unauthorized user ${event.user}`);
50
+ return;
51
+ }
52
+ const sessionKey = deriveSessionKey(event, {
53
+ shareSessionInChannel: this.shareSessionInChannel,
54
+ });
55
+ const replyContext = buildReplyContext(event);
25
56
  // Download attachments if present
26
57
  const attachments = [];
27
58
  if (event.files) {
@@ -41,7 +72,11 @@ export class SlackConnector {
41
72
  }
42
73
  }
43
74
  const msg = {
75
+ connector: this.name,
44
76
  source: "slack",
77
+ sessionKey,
78
+ replyContext,
79
+ messageId: event.ts,
45
80
  channel: event.channel,
46
81
  thread: event.thread_ts,
47
82
  user: event.user,
@@ -49,16 +84,41 @@ export class SlackConnector {
49
84
  text: event.text || "",
50
85
  attachments,
51
86
  raw: event,
87
+ transportMeta: {
88
+ channelType: event.channel_type || "channel",
89
+ team: event.team || null,
90
+ },
52
91
  };
53
92
  this.handler(msg);
54
93
  });
55
94
  await this.app.start();
95
+ this.started = true;
96
+ this.lastError = null;
56
97
  logger.info("Slack connector started (socket mode)");
57
98
  }
58
99
  async stop() {
59
100
  await this.app.stop();
101
+ this.started = false;
60
102
  logger.info("Slack connector stopped");
61
103
  }
104
+ getCapabilities() {
105
+ return this.capabilities;
106
+ }
107
+ getHealth() {
108
+ return {
109
+ status: this.lastError ? "error" : this.started ? "running" : "stopped",
110
+ detail: this.lastError ?? undefined,
111
+ capabilities: this.capabilities,
112
+ };
113
+ }
114
+ reconstructTarget(replyContext) {
115
+ return {
116
+ channel: typeof replyContext.channel === "string" ? replyContext.channel : "",
117
+ thread: typeof replyContext.thread === "string" ? replyContext.thread : undefined,
118
+ messageTs: typeof replyContext.messageTs === "string" ? replyContext.messageTs : undefined,
119
+ replyContext,
120
+ };
121
+ }
62
122
  async sendMessage(target, text) {
63
123
  if (!text || !text.trim())
64
124
  return undefined;
@@ -69,7 +129,24 @@ export class SlackConnector {
69
129
  continue;
70
130
  const res = await this.app.client.chat.postMessage({
71
131
  channel: target.channel,
72
- thread_ts: target.thread,
132
+ text: chunk,
133
+ });
134
+ lastTs = res.ts;
135
+ }
136
+ return lastTs;
137
+ }
138
+ async replyMessage(target, text) {
139
+ if (!text || !text.trim())
140
+ return undefined;
141
+ const threadTs = target.thread || target.messageTs;
142
+ const chunks = formatResponse(text);
143
+ let lastTs;
144
+ for (const chunk of chunks) {
145
+ if (!chunk.trim())
146
+ continue;
147
+ const res = await this.app.client.chat.postMessage({
148
+ channel: target.channel,
149
+ thread_ts: threadTs,
73
150
  text: chunk,
74
151
  });
75
152
  lastTs = res.ts;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/connectors/slack/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/connectors/slack/index.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,15 @@
1
- export declare function deriveSourceRef(event: any): string;
1
+ import type { ReplyContext } from "../../shared/types.js";
2
+ export interface SlackMessageEventLike {
3
+ channel: string;
4
+ user?: string;
5
+ ts?: string;
6
+ thread_ts?: string;
7
+ channel_type?: string;
8
+ }
9
+ export interface SlackThreadOptions {
10
+ shareSessionInChannel?: boolean;
11
+ }
12
+ export declare function deriveSessionKey(event: SlackMessageEventLike, opts?: SlackThreadOptions): string;
13
+ export declare function buildReplyContext(event: SlackMessageEventLike): ReplyContext;
14
+ export declare function isOldSlackMessage(ts: string | undefined, bootTimeMs: number): boolean;
2
15
  //# sourceMappingURL=threads.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/threads.ts"],"names":[],"mappings":"AAAA,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAQlD"}
1
+ {"version":3,"file":"threads.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/threads.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,EAC5B,IAAI,GAAE,kBAAuB,GAC5B,MAAM,CAcR;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,qBAAqB,GAAG,YAAY,CAU5E;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAKrF"}
@@ -1,10 +1,31 @@
1
- export function deriveSourceRef(event) {
1
+ export function deriveSessionKey(event, opts = {}) {
2
2
  if (event.channel_type === "im") {
3
- return `slack:dm:${event.user}`;
3
+ return `slack:dm:${event.user || "unknown"}`;
4
+ }
5
+ if (opts.shareSessionInChannel) {
6
+ return `slack:${event.channel}`;
4
7
  }
5
8
  if (event.thread_ts && event.thread_ts !== event.ts) {
6
9
  return `slack:${event.channel}:${event.thread_ts}`;
7
10
  }
8
11
  return `slack:${event.channel}`;
9
12
  }
13
+ export function buildReplyContext(event) {
14
+ const thread = event.thread_ts && event.thread_ts !== event.ts
15
+ ? event.thread_ts
16
+ : undefined;
17
+ return {
18
+ channel: event.channel,
19
+ thread: thread ?? null,
20
+ messageTs: event.ts ?? null,
21
+ };
22
+ }
23
+ export function isOldSlackMessage(ts, bootTimeMs) {
24
+ if (!ts)
25
+ return false;
26
+ const secs = Number(ts.split(".")[0]);
27
+ if (!Number.isFinite(secs))
28
+ return false;
29
+ return secs * 1000 < bootTimeMs;
30
+ }
10
31
  //# sourceMappingURL=threads.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"threads.js","sourceRoot":"","sources":["../../../../src/connectors/slack/threads.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,eAAe,CAAC,KAAU;IACxC,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QAChC,OAAO,YAAY,KAAK,CAAC,IAAI,EAAE,CAAC;IAClC,CAAC;IACD,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,EAAE,CAAC;QACpD,OAAO,SAAS,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;IACrD,CAAC;IACD,OAAO,SAAS,KAAK,CAAC,OAAO,EAAE,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"threads.js","sourceRoot":"","sources":["../../../../src/connectors/slack/threads.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,gBAAgB,CAC9B,KAA4B,EAC5B,OAA2B,EAAE;IAE7B,IAAI,KAAK,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QAChC,OAAO,YAAY,KAAK,CAAC,IAAI,IAAI,SAAS,EAAE,CAAC;IAC/C,CAAC;IAED,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC/B,OAAO,SAAS,KAAK,CAAC,OAAO,EAAE,CAAC;IAClC,CAAC;IAED,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE,EAAE,CAAC;QACpD,OAAO,SAAS,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;IACrD,CAAC;IAED,OAAO,SAAS,KAAK,CAAC,OAAO,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAA4B;IAC5D,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,KAAK,CAAC,EAAE;QAC5D,CAAC,CAAC,KAAK,CAAC,SAAS;QACjB,CAAC,CAAC,SAAS,CAAC;IAEd,OAAO;QACL,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,MAAM,EAAE,MAAM,IAAI,IAAI;QACtB,SAAS,EAAE,KAAK,CAAC,EAAE,IAAI,IAAI;KAC5B,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAsB,EAAE,UAAkB;IAC1E,IAAI,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IACtB,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACzC,OAAO,IAAI,GAAG,IAAI,GAAG,UAAU,CAAC;AAClC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=threads.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threads.test.d.ts","sourceRoot":"","sources":["../../../../src/connectors/slack/threads.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { buildReplyContext, deriveSessionKey, isOldSlackMessage } from "./threads.js";
4
+ test("deriveSessionKey keeps DM sessions per user", () => {
5
+ const key = deriveSessionKey({
6
+ channel: "D123",
7
+ user: "U123",
8
+ channel_type: "im",
9
+ ts: "1700000000.000100",
10
+ });
11
+ assert.equal(key, "slack:dm:U123");
12
+ });
13
+ test("deriveSessionKey isolates thread replies unless channel sharing is enabled", () => {
14
+ const threaded = deriveSessionKey({
15
+ channel: "C123",
16
+ user: "U123",
17
+ ts: "1700000100.000200",
18
+ thread_ts: "1700000000.000100",
19
+ });
20
+ const shared = deriveSessionKey({
21
+ channel: "C123",
22
+ user: "U123",
23
+ ts: "1700000100.000200",
24
+ thread_ts: "1700000000.000100",
25
+ }, { shareSessionInChannel: true });
26
+ assert.equal(threaded, "slack:C123:1700000000.000100");
27
+ assert.equal(shared, "slack:C123");
28
+ });
29
+ test("buildReplyContext keeps the original message id for replies", () => {
30
+ const context = buildReplyContext({
31
+ channel: "C123",
32
+ ts: "1700000100.000200",
33
+ thread_ts: "1700000000.000100",
34
+ });
35
+ assert.deepEqual(context, {
36
+ channel: "C123",
37
+ thread: "1700000000.000100",
38
+ messageTs: "1700000100.000200",
39
+ });
40
+ });
41
+ test("isOldSlackMessage compares against boot time", () => {
42
+ assert.equal(isOldSlackMessage("1700000000.000100", 1700000001000), true);
43
+ assert.equal(isOldSlackMessage("1700000002.000100", 1700000001000), false);
44
+ });
45
+ //# sourceMappingURL=threads.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threads.test.js","sourceRoot":"","sources":["../../../../src/connectors/slack/threads.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEtF,IAAI,CAAC,6CAA6C,EAAE,GAAG,EAAE;IACvD,MAAM,GAAG,GAAG,gBAAgB,CAAC;QAC3B,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,MAAM;QACZ,YAAY,EAAE,IAAI;QAClB,EAAE,EAAE,mBAAmB;KACxB,CAAC,CAAC;IAEH,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4EAA4E,EAAE,GAAG,EAAE;IACtF,MAAM,QAAQ,GAAG,gBAAgB,CAAC;QAChC,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,MAAM;QACZ,EAAE,EAAE,mBAAmB;QACvB,SAAS,EAAE,mBAAmB;KAC/B,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,gBAAgB,CAC7B;QACE,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,MAAM;QACZ,EAAE,EAAE,mBAAmB;QACvB,SAAS,EAAE,mBAAmB;KAC/B,EACD,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAChC,CAAC;IAEF,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,8BAA8B,CAAC,CAAC;IACvD,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACrC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6DAA6D,EAAE,GAAG,EAAE;IACvE,MAAM,OAAO,GAAG,iBAAiB,CAAC;QAChC,OAAO,EAAE,MAAM;QACf,EAAE,EAAE,mBAAmB;QACvB,SAAS,EAAE,mBAAmB;KAC/B,CAAC,CAAC;IAEH,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE;QACxB,OAAO,EAAE,MAAM;QACf,MAAM,EAAE,mBAAmB;QAC3B,SAAS,EAAE,mBAAmB;KAC/B,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,8CAA8C,EAAE,GAAG,EAAE;IACxD,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,mBAAmB,EAAE,aAAa,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1E,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,mBAAmB,EAAE,aAAa,CAAC,EAAE,KAAK,CAAC,CAAC;AAC7E,CAAC,CAAC,CAAC"}
@@ -1,3 +1,4 @@
1
- import type { CronJob, Engine, JinnConfig, Connector } from "../shared/types.js";
2
- export declare function runCronJob(job: CronJob, engines: Map<string, Engine>, config: JinnConfig, connectors: Map<string, Connector>): Promise<void>;
1
+ import type { CronJob, Connector, JinnConfig } from "../shared/types.js";
2
+ import type { SessionManager } from "../sessions/manager.js";
3
+ export declare function runCronJob(job: CronJob, sessionManager: SessionManager, config: JinnConfig, connectors: Map<string, Connector>): Promise<void>;
3
4
  //# sourceMappingURL=runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/cron/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAQjF,wBAAsB,UAAU,CAC9B,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CAmIf"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/cron/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAKzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,wBAAsB,UAAU,CAC9B,GAAG,EAAE,OAAO,EACZ,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CAiFf"}
@@ -1,118 +1,77 @@
1
- import { buildContext } from "../sessions/context.js";
2
- import { createSession, updateSession, insertMessage } from "../sessions/registry.js";
3
- import { JINN_HOME } from "../shared/paths.js";
4
1
  import { logger } from "../shared/logger.js";
5
2
  import { appendRunLog } from "./jobs.js";
6
3
  import { scanOrg, findEmployee } from "../gateway/org.js";
7
- export async function runCronJob(job, engines, config, connectors) {
4
+ import { CronConnector } from "../connectors/cron/index.js";
5
+ export async function runCronJob(job, sessionManager, config, connectors) {
8
6
  const startTime = Date.now();
9
7
  logger.info(`Cron job "${job.name}" (${job.id}) starting`);
10
- // 1. Determine engine + model
11
- const engineName = job.engine || config.engines.default;
12
- const engine = engines.get(engineName);
13
- if (!engine) {
14
- logger.error(`Engine "${engineName}" not found for cron job "${job.name}"`);
15
- return;
16
- }
17
- const model = job.model || config.engines[engineName]?.model;
18
- // 2. Warn if a non-COO employee has delivery configured (anti-pattern)
19
8
  const delivery = job.delivery || config.cron?.defaultDelivery;
20
9
  const cooSlug = config.portal?.portalName?.toLowerCase() || "jinn";
21
10
  if (delivery && job.employee && job.employee !== cooSlug) {
22
11
  logger.warn(`Cron job "${job.name}" targets employee "${job.employee}" with delivery to ${delivery.connector}:${delivery.channel}. ` +
23
12
  `Recommended pattern: target "${cooSlug}" and let the COO delegate to "${job.employee}" via a child session for output review/filtering.`);
24
13
  }
25
- // 3. Resolve employee
26
14
  let employee;
27
15
  if (job.employee) {
28
16
  const orgRegistry = scanOrg();
29
17
  employee = findEmployee(job.employee, orgRegistry);
30
18
  }
31
- // 4. Create a proper session in the DB
32
- const sourceRef = `cron:${job.id}`;
33
- const session = createSession({
34
- engine: engineName,
35
- source: "cron",
36
- sourceRef,
37
- employee: employee?.name,
38
- model,
39
- title: job.name,
40
- prompt: job.prompt,
41
- portalName: config.portal?.portalName,
42
- });
43
- insertMessage(session.id, "user", job.prompt);
44
- // 5. Build context
45
- const ctx = buildContext({
46
- source: "cron",
47
- channel: job.id,
48
- user: "system",
49
- employee,
50
- config,
51
- connectors: Array.from(connectors.keys()),
52
- sessionId: session.id,
53
- });
54
- updateSession(session.id, {
55
- status: "running",
56
- lastActivity: new Date().toISOString(),
57
- });
58
- // 6. Run engine (fresh session, no resume)
19
+ const connector = new CronConnector(connectors, delivery);
20
+ const startedAt = new Date().toISOString();
21
+ const sessionKey = `cron:${job.id}:${Date.now()}`;
59
22
  try {
60
- const result = await engine.run({
61
- prompt: job.prompt,
62
- systemPrompt: ctx,
63
- cwd: JINN_HOME,
64
- model,
65
- });
66
- const durationMs = Date.now() - startTime;
67
- const responseText = result.result?.trim()
68
- ? result.result
69
- : result.error || "(No response from engine)";
70
- // Persist assistant response
71
- insertMessage(session.id, "assistant", responseText);
72
- // Update session with engine session id
73
- updateSession(session.id, {
74
- engineSessionId: result.sessionId,
75
- status: result.error ? "error" : "idle",
76
- lastActivity: new Date().toISOString(),
77
- lastError: result.error ?? null,
23
+ await sessionManager.route({
24
+ connector: connector.name,
25
+ source: "cron",
26
+ sessionKey,
27
+ replyContext: {
28
+ channel: delivery?.channel || job.id,
29
+ messageTs: null,
30
+ cronJobId: job.id,
31
+ cronJobName: job.name,
32
+ deliveryConnector: delivery?.connector ?? null,
33
+ },
34
+ messageId: undefined,
35
+ channel: delivery?.channel || job.id,
36
+ thread: undefined,
37
+ user: "system",
38
+ userId: "system",
39
+ text: job.prompt,
40
+ attachments: [],
41
+ raw: { jobId: job.id, trigger: "cron" },
42
+ transportMeta: {
43
+ cronJobId: job.id,
44
+ cronJobName: job.name,
45
+ deliveryConnector: delivery?.connector ?? null,
46
+ deliveryChannel: delivery?.channel ?? null,
47
+ },
48
+ }, connector, {
49
+ employee,
50
+ engine: job.engine || config.engines.default,
51
+ model: job.model || config.engines[(job.engine || config.engines.default)]?.model,
52
+ title: job.name,
78
53
  });
79
- // 7. If delivery configured (job-level or default), send result to connector
80
- if (delivery && result.result) {
81
- const connector = connectors.get(delivery.connector);
82
- if (connector) {
83
- await connector.sendMessage({ channel: delivery.channel }, result.result);
84
- }
85
- else {
86
- logger.warn(`Delivery connector "${delivery.connector}" not found`);
87
- }
88
- }
89
- // 8. Log run
90
54
  appendRunLog(job.id, {
91
- timestamp: new Date().toISOString(),
92
- sessionId: session.id,
93
- status: result.error ? "error" : "success",
94
- durationMs,
95
- error: result.error || null,
96
- resultPreview: result.result?.slice(0, 500) || null,
55
+ timestamp: startedAt,
56
+ sessionKey,
57
+ status: "success",
58
+ durationMs: Date.now() - startTime,
59
+ error: null,
60
+ resultPreview: null,
97
61
  });
98
- logger.info(`Cron job "${job.name}" completed in ${durationMs}ms`);
62
+ logger.info(`Cron job "${job.name}" completed in ${Date.now() - startTime}ms`);
99
63
  }
100
64
  catch (err) {
101
- const durationMs = Date.now() - startTime;
102
- updateSession(session.id, {
103
- status: "error",
104
- lastActivity: new Date().toISOString(),
105
- lastError: err.message,
106
- });
65
+ const message = err instanceof Error ? err.message : String(err);
107
66
  appendRunLog(job.id, {
108
- timestamp: new Date().toISOString(),
109
- sessionId: session.id,
67
+ timestamp: startedAt,
68
+ sessionKey,
110
69
  status: "error",
111
- durationMs,
112
- error: err.message,
70
+ durationMs: Date.now() - startTime,
71
+ error: message,
113
72
  resultPreview: null,
114
73
  });
115
- logger.error(`Cron job "${job.name}" failed: ${err.message}`);
74
+ logger.error(`Cron job "${job.name}" failed: ${message}`);
116
75
  }
117
76
  }
118
77
  //# sourceMappingURL=runner.js.map