hoomanjs 1.17.0 → 1.17.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hoomanjs",
3
- "version": "1.17.0",
3
+ "version": "1.17.1",
4
4
  "description": "Hackable Bun-powered AI agent toolkit for building local CLI, ACP, MCP, and channel-driven workflows.",
5
5
  "author": {
6
6
  "name": "Vaibhav Pandey",
@@ -53,15 +53,9 @@ export async function create(
53
53
  const ltm = config.tools.ltm.enabled
54
54
  ? createLongTermMemoryStore(config)
55
55
  : null;
56
- const skills = config.tools.skills.enabled
57
- ? (await createSkillsPrompt(registry)).content
58
- : "";
59
- const prefixed = config.tools.mcp.enabled
60
- ? await mcp.manager.listPrefixedTools()
61
- : [];
62
- const append = config.tools.mcp.enabled
63
- ? await mcp.manager.listServerInstructions()
64
- : [];
56
+ const skills = (await createSkillsPrompt(registry)).content;
57
+ const prefixed = await mcp.manager.listPrefixedTools();
58
+ const append = await mcp.manager.listServerInstructions();
65
59
  const prompt = [system.content, meta.systemPrompt, ...append, skills]
66
60
  .filter((x) => !!x)
67
61
  .join(SECTION_BREAK);
@@ -15,16 +15,11 @@ export const HOOMAN_CHANNEL_PERMISSION = "hooman/channel/permission";
15
15
  const HOOMAN_CHANNEL_PERMISSION_METHOD = `notifications/${HOOMAN_CHANNEL_PERMISSION}`;
16
16
 
17
17
  export type ChannelMessageMeta = {
18
- server: string;
19
- channel: string;
20
- method: string;
21
- params: unknown;
18
+ subscription: ChannelSubscription;
22
19
  source?: string;
23
- identity: {
24
- user?: string;
25
- session?: string;
26
- thread?: string;
27
- };
20
+ user?: string;
21
+ session?: string;
22
+ thread?: string;
28
23
  };
29
24
 
30
25
  export type ChannelMessage = {
@@ -325,7 +320,7 @@ export class Manager {
325
320
  params?: unknown;
326
321
  }) => {
327
322
  const { method, params } = notification;
328
- const prompt = this.toChannelPrompt(method, params);
323
+ const prompt = this.toChannelPrompt(params);
329
324
  if (!prompt) {
330
325
  return;
331
326
  }
@@ -335,16 +330,11 @@ export class Manager {
335
330
  prompt,
336
331
  attachments,
337
332
  meta: {
338
- server,
339
- channel,
340
- method,
341
- params,
333
+ subscription: { server, channel },
342
334
  source: readSourceValue(params),
343
- identity: {
344
- user: readPathValue(params, user),
345
- session: readPathValue(params, session),
346
- thread: readPathValue(params, thread),
347
- },
335
+ user: readPathValue(params, user),
336
+ session: readPathValue(params, session),
337
+ thread: readPathValue(params, thread),
348
338
  },
349
339
  });
350
340
  };
@@ -463,20 +453,39 @@ export class Manager {
463
453
  }
464
454
  }
465
455
 
466
- private toChannelPrompt(method: string, params?: unknown): string {
456
+ private toChannelPrompt(params?: unknown): string {
457
+ const parts = [];
458
+
467
459
  if (
468
460
  params &&
469
461
  typeof params === "object" &&
470
462
  "content" in params &&
471
463
  typeof params.content === "string"
472
464
  ) {
473
- return params.content.trim();
465
+ parts.push(params.content.trim());
474
466
  }
475
467
 
476
- try {
477
- return JSON.stringify(params).trim();
478
- } catch {
479
- return String(params).trim();
468
+ if (
469
+ params &&
470
+ typeof params === "object" &&
471
+ "attachments" in params &&
472
+ Array.isArray(params.attachments) &&
473
+ params.attachments.length > 0
474
+ ) {
475
+ parts.push("User sent attachments.");
480
476
  }
477
+
478
+ if (
479
+ params &&
480
+ typeof params === "object" &&
481
+ "event" in params &&
482
+ typeof params.event === "object"
483
+ ) {
484
+ parts.push(
485
+ "Raw event data:\n```json\n" + JSON.stringify(params.event) + "\n```",
486
+ );
487
+ }
488
+
489
+ return parts.filter(Boolean).join("\n").trim();
481
490
  }
482
491
  }
@@ -12,6 +12,8 @@ type EnvironmentPromptContext = {
12
12
  osVersion: string;
13
13
  shell: string;
14
14
  isGitRepo: boolean;
15
+ currentDateTime: string;
16
+ timeZone: string;
15
17
  };
16
18
 
17
19
  function detectPlatform(): string {
@@ -50,13 +52,20 @@ function detectGitRepo(startDir: string): boolean {
50
52
  }
51
53
  }
52
54
 
55
+ function detectTimeZone(): string {
56
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "unknown";
57
+ }
58
+
53
59
  export function getEnvironmentPromptContext(): EnvironmentPromptContext {
54
60
  const cwd = process.cwd();
61
+ const now = new Date();
55
62
  return {
56
63
  cwd,
57
64
  platform: detectPlatform(),
58
65
  osVersion: detectOsVersion(),
59
66
  shell: detectShell(),
60
67
  isGitRepo: detectGitRepo(cwd),
68
+ currentDateTime: now.toString(),
69
+ timeZone: detectTimeZone(),
61
70
  };
62
71
  }
@@ -7,9 +7,11 @@ You are running in the following runtime environment:
7
7
  - Shell: `{{ environment.shell }}`
8
8
  - OS version: `{{ environment.osVersion }}`
9
9
  - Is git repository: `{{ environment.isGitRepo }}`
10
+ - Current date/time: `{{ environment.currentDateTime }}`
11
+ - Time zone: `{{ environment.timeZone }}`
10
12
 
11
13
  ### How To Use This
12
14
 
13
15
  - Use this information to choose correct path handling, shell syntax, and platform-specific behavior
14
- - Treat this section as runtime context, not real-time clock data
15
- - If the task needs the current date or time, call `get_current_time` instead of inferring from this section
16
+ - Treat this section as runtime context captured when the prompt was built
17
+ - If the task needs precise current time during a later turn, call `get_current_time`
@@ -32,23 +32,23 @@ function resolveSessionId(
32
32
  message: ChannelMessage,
33
33
  fallback?: string,
34
34
  ): string | undefined {
35
- const raw = message.meta.identity.session?.trim() || fallback;
35
+ const raw = message.meta.session?.trim() || fallback;
36
36
  if (!raw) return undefined;
37
37
  // Namespace per `server:channel` so the same chat id coming from two
38
38
  // different MCP servers (or two channels on the same server) never collide.
39
- return `${message.meta.server}:${message.meta.channel}:${raw}`;
39
+ return `${message.meta.subscription.server}:${message.meta.subscription.channel}:${raw}`;
40
40
  }
41
41
 
42
42
  function resolveUserId(
43
43
  message: ChannelMessage,
44
44
  session?: string,
45
45
  ): string | undefined {
46
- const raw = message.meta.identity.user?.trim();
46
+ const raw = message.meta.user?.trim();
47
47
  if (!raw) return session;
48
48
  // Same user id across different servers is not the same human, so scope
49
49
  // user ids by server. Channel is intentionally omitted so long-term memory
50
50
  // can stay consistent for a user across rooms within one server.
51
- return `${message.meta.server}:${raw}`;
51
+ return `${message.meta.subscription.server}:${raw}`;
52
52
  }
53
53
 
54
54
  function formatSubscriptions(
@@ -91,7 +91,7 @@ export async function main(options: RunDaemonOptions): Promise<void> {
91
91
 
92
92
  const [queue, stop] = await createQueue(
93
93
  async (message: ChannelMessage) => {
94
- const tag = `${message.meta.server}:${message.meta.channel}`;
94
+ const tag = `${message.meta.subscription.server}:${message.meta.subscription.channel}`;
95
95
  const session = resolveSessionId(message, options.session);
96
96
  const user = resolveUserId(message, session);
97
97
 
@@ -103,18 +103,12 @@ export async function main(options: RunDaemonOptions): Promise<void> {
103
103
  options.agent.appState.set("userId", user);
104
104
  options.agent.appState.set("sessionId", session);
105
105
  const origin = {
106
- server: message.meta.server,
107
- channel: message.meta.channel,
106
+ server: message.meta.subscription.server,
107
+ channel: message.meta.subscription.channel,
108
108
  ...(message.meta.source ? { source: message.meta.source } : {}),
109
- ...(message.meta.identity.user
110
- ? { user: message.meta.identity.user }
111
- : {}),
112
- ...(message.meta.identity.session
113
- ? { session: message.meta.identity.session }
114
- : {}),
115
- ...(message.meta.identity.thread
116
- ? { thread: message.meta.identity.thread }
117
- : {}),
109
+ ...(message.meta.user ? { user: message.meta.user } : {}),
110
+ ...(message.meta.session ? { session: message.meta.session } : {}),
111
+ ...(message.meta.thread ? { thread: message.meta.thread } : {}),
118
112
  };
119
113
  options.agent.appState.set("origin", {
120
114
  ...origin,
@@ -143,7 +137,7 @@ export async function main(options: RunDaemonOptions): Promise<void> {
143
137
  channels,
144
138
  (message) => {
145
139
  debug(
146
- `received notification → ${message.meta.server}:${message.meta.channel}`,
140
+ `received notification → ${message.meta.subscription.server}:${message.meta.subscription.channel}`,
147
141
  );
148
142
  void queue.push(message);
149
143
  },