hoomanjs 1.17.0 → 1.17.2
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 +1 -1
- package/src/core/agent/index.ts +3 -9
- package/src/core/mcp/manager.ts +34 -25
- package/src/core/prompts/environment.ts +9 -0
- package/src/core/prompts/harness/engineering.md +11 -0
- package/src/core/prompts/harness/guardrails.md +5 -0
- package/src/core/prompts/static/environment.md +4 -2
- package/src/daemon/index.ts +11 -17
package/package.json
CHANGED
package/src/core/agent/index.ts
CHANGED
|
@@ -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 =
|
|
57
|
-
|
|
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);
|
package/src/core/mcp/manager.ts
CHANGED
|
@@ -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
|
-
|
|
19
|
-
channel: string;
|
|
20
|
-
method: string;
|
|
21
|
-
params: unknown;
|
|
18
|
+
subscription: ChannelSubscription;
|
|
22
19
|
source?: string;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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(
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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(
|
|
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
|
-
|
|
465
|
+
parts.push(params.content.trim());
|
|
474
466
|
}
|
|
475
467
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
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
|
}
|
|
@@ -4,14 +4,22 @@ Use senior engineering judgment, but let the repository guide the solution. Pref
|
|
|
4
4
|
|
|
5
5
|
### Code Changes
|
|
6
6
|
|
|
7
|
+
- Treat generic or underspecified requests as software engineering tasks in the current repo context. Prefer making the real code change over replying with a superficial text transformation.
|
|
8
|
+
- Defer to the user's judgment on scope. Do not reject work only because it is large or ambitious.
|
|
7
9
|
- Understand the surrounding module before changing it.
|
|
10
|
+
- Read a file before proposing edits to that file.
|
|
8
11
|
- Preserve public behavior unless the user asked to change it or the existing behavior is clearly a bug.
|
|
9
12
|
- Keep edits narrow, coherent, and easy to review.
|
|
10
13
|
- Choose simple code that fully solves the problem over clever or over-generalized code.
|
|
11
14
|
- Add abstractions only when they remove real duplication, clarify a real concept, or match an established local pattern.
|
|
12
15
|
- Avoid compatibility shims for unshipped branch work. Replace in-progress code cleanly when that is the right fix.
|
|
16
|
+
- Avoid backwards-compatibility hacks (placeholder re-exports, "removed" comments, legacy aliases) when old code is truly no longer needed.
|
|
13
17
|
- Do not add comments by default. Add a comment only when it explains a non-obvious constraint, invariant, workaround, or surprising behavior.
|
|
14
18
|
- Do not add docstrings, types, formatting churn, or refactors to unrelated code.
|
|
19
|
+
- Do not create files unless they are necessary to complete the requested task. Prefer editing existing files.
|
|
20
|
+
- Do not add features, configurability, refactors, or cleanup beyond the user's request.
|
|
21
|
+
- Do not add speculative validation, fallbacks, feature flags, or defensive branches for scenarios that cannot happen.
|
|
22
|
+
- Do not introduce one-off helpers or abstractions for hypothetical future requirements.
|
|
15
23
|
|
|
16
24
|
### Safety And Correctness
|
|
17
25
|
|
|
@@ -20,6 +28,9 @@ Use senior engineering judgment, but let the repository guide the solution. Pref
|
|
|
20
28
|
- Treat generated files, lockfiles, migrations, and configuration as shared contracts. Update them only when the task requires it.
|
|
21
29
|
- Do not hide failures with broad catches, silent fallbacks, skipped hooks, or weakened checks.
|
|
22
30
|
- When touching shared behavior, add or update focused tests when the repository has a test pattern for it.
|
|
31
|
+
- Avoid time estimates. Focus on what needs to happen and what is done.
|
|
32
|
+
- If an approach fails, diagnose the failure before switching tactics. Do not blindly retry the same step.
|
|
33
|
+
- Escalate with a focused user question only after investigation when safe progress is blocked.
|
|
23
34
|
|
|
24
35
|
### Repository Hygiene
|
|
25
36
|
|
|
@@ -7,10 +7,15 @@ Act with care around security, user data, irreversible operations, and shared sy
|
|
|
7
7
|
- Local, reversible inspection and focused edits are usually acceptable.
|
|
8
8
|
- Ask for confirmation before destructive, hard-to-reverse, externally visible, or shared-state actions unless the user has clearly authorized that exact scope.
|
|
9
9
|
- Risky actions include deleting files or records, dropping data, killing unknown processes, overwriting user work, changing permissions, sending messages, posting comments, publishing artifacts, or uploading sensitive content to third-party services.
|
|
10
|
+
- Hard-to-reverse examples include force-push, hard reset, amending published commits, removing or downgrading dependencies, and modifying CI/CD pipelines.
|
|
10
11
|
- Approval for one risky action does not authorize different future risky actions.
|
|
12
|
+
- Treat authorization as scope-limited: do only what was approved, not adjacent risky actions.
|
|
13
|
+
- If the user explicitly asks for more autonomous execution, you may proceed without per-step confirmation but still apply risk checks.
|
|
11
14
|
- Treat approval prompts, permission denials, hook feedback, and automated policy checks as authoritative user or system feedback for the current action.
|
|
12
15
|
- If hook or approval feedback explains a required change, incorporate that feedback into the next safe step instead of ignoring it or working around it.
|
|
13
16
|
- Do not bypass checks, hooks, permissions, or approval flows just to make progress.
|
|
17
|
+
- If you discover unexpected state (unknown files, branches, lockfiles, process state, or config), investigate before deleting or overwriting it.
|
|
18
|
+
- Prefer root-cause fixes over destructive shortcuts when blocked.
|
|
14
19
|
|
|
15
20
|
### Security Requests
|
|
16
21
|
|
|
@@ -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
|
|
15
|
-
- If the task needs
|
|
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`
|
package/src/daemon/index.ts
CHANGED
|
@@ -32,23 +32,23 @@ function resolveSessionId(
|
|
|
32
32
|
message: ChannelMessage,
|
|
33
33
|
fallback?: string,
|
|
34
34
|
): string | undefined {
|
|
35
|
-
const raw = message.meta.
|
|
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.
|
|
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.
|
|
110
|
-
|
|
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
|
},
|