@hmharness/agent 0.14.16 → 0.14.17

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/dist/acp.d.ts ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @hmharness/agent - ACP Server/Client (P2-01 + P2-02)
3
+ *
4
+ * The audit called for ACP (Agent Communication Protocol) making hmharness
5
+ * "可被 IDE/Agent 调用的标准工程 Runtime" and A2A for "外部 Agent 以
6
+ * task/plan/artifact/evidence 方式协作".
7
+ *
8
+ * This module provides:
9
+ * 1. AcpServer: exposes hmharness capabilities to external agents/IDEs
10
+ * 2. AcpClient: calls external agents from hmharness
11
+ * 3. Task/Plan/Artifact/Evidence message types for A2A collaboration
12
+ */
13
+ export type AcpMessageType = 'task' | 'plan' | 'artifact' | 'evidence' | 'query' | 'response' | 'error';
14
+ export interface AcpMessage {
15
+ id: string;
16
+ type: AcpMessageType;
17
+ from: string;
18
+ to: string;
19
+ timestamp: string;
20
+ payload: Record<string, unknown>;
21
+ }
22
+ export interface AcpTask {
23
+ description: string;
24
+ priority: 'low' | 'medium' | 'high' | 'critical';
25
+ deadline?: string;
26
+ constraints?: string[];
27
+ }
28
+ export interface AcpPlan {
29
+ steps: Array<{
30
+ id: string;
31
+ description: string;
32
+ status: 'pending' | 'running' | 'done' | 'failed';
33
+ }>;
34
+ estimatedDuration?: number;
35
+ }
36
+ export interface AcpArtifact {
37
+ kind: 'code' | 'document' | 'config' | 'test' | 'report';
38
+ path: string;
39
+ content: string;
40
+ hash?: string;
41
+ }
42
+ export interface AcpServerConfig {
43
+ /** server identity */
44
+ agentId: string;
45
+ /** capabilities this server exposes */
46
+ capabilities: string[];
47
+ /** how to reach this server */
48
+ endpoint: string;
49
+ }
50
+ export interface AcpClientConfig {
51
+ /** client identity */
52
+ agentId: string;
53
+ /** known servers */
54
+ servers: Map<string, AcpServerConfig>;
55
+ }
56
+ /**
57
+ * Create an ACP message.
58
+ * Pure - testable.
59
+ */
60
+ export declare function createMessage(type: AcpMessageType, from: string, to: string, payload: Record<string, unknown>): AcpMessage;
61
+ /**
62
+ * Validate an ACP message.
63
+ * Pure - testable.
64
+ */
65
+ export declare function validateMessage(msg: unknown): {
66
+ valid: boolean;
67
+ errors: string[];
68
+ };
69
+ /**
70
+ * ACP server handler registry - routes incoming messages to handlers.
71
+ */
72
+ export declare class AcpServer {
73
+ readonly config: AcpServerConfig;
74
+ private handlers;
75
+ constructor(config: AcpServerConfig);
76
+ on(type: AcpMessageType, handler: (msg: AcpMessage) => Promise<AcpMessage>): void;
77
+ handle(msg: AcpMessage): Promise<AcpMessage>;
78
+ /** Advertise capabilities as a capability document */
79
+ capabilities(): Record<string, unknown>;
80
+ }
81
+ /**
82
+ * ACP client - sends messages to servers and awaits responses.
83
+ */
84
+ export declare class AcpClient {
85
+ readonly config: AcpClientConfig;
86
+ constructor(config: AcpClientConfig);
87
+ send(to: string, type: AcpMessageType, payload: Record<string, unknown>): Promise<AcpMessage>;
88
+ /** Discover servers by capability */
89
+ discoverByCapability(capability: string): AcpServerConfig[];
90
+ }
package/dist/acp.js ADDED
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @hmharness/agent - ACP Server/Client (P2-01 + P2-02)
3
+ *
4
+ * The audit called for ACP (Agent Communication Protocol) making hmharness
5
+ * "可被 IDE/Agent 调用的标准工程 Runtime" and A2A for "外部 Agent 以
6
+ * task/plan/artifact/evidence 方式协作".
7
+ *
8
+ * This module provides:
9
+ * 1. AcpServer: exposes hmharness capabilities to external agents/IDEs
10
+ * 2. AcpClient: calls external agents from hmharness
11
+ * 3. Task/Plan/Artifact/Evidence message types for A2A collaboration
12
+ */
13
+ /**
14
+ * Create an ACP message.
15
+ * Pure - testable.
16
+ */
17
+ export function createMessage(type, from, to, payload) {
18
+ return {
19
+ id: `msg-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`,
20
+ type, from, to,
21
+ timestamp: new Date().toISOString(),
22
+ payload,
23
+ };
24
+ }
25
+ /**
26
+ * Validate an ACP message.
27
+ * Pure - testable.
28
+ */
29
+ export function validateMessage(msg) {
30
+ const errors = [];
31
+ const m = msg;
32
+ if (!m.id)
33
+ errors.push('missing id');
34
+ if (!m.type)
35
+ errors.push('missing type');
36
+ if (!m.from)
37
+ errors.push('missing from');
38
+ if (!m.to)
39
+ errors.push('missing to');
40
+ if (!m.timestamp)
41
+ errors.push('missing timestamp');
42
+ if (m.payload === undefined)
43
+ errors.push('missing payload');
44
+ return { valid: errors.length === 0, errors };
45
+ }
46
+ /**
47
+ * ACP server handler registry - routes incoming messages to handlers.
48
+ */
49
+ export class AcpServer {
50
+ config;
51
+ handlers = new Map();
52
+ constructor(config) {
53
+ this.config = config;
54
+ }
55
+ on(type, handler) {
56
+ this.handlers.set(type, handler);
57
+ }
58
+ async handle(msg) {
59
+ const handler = this.handlers.get(msg.type);
60
+ if (!handler) {
61
+ return createMessage('error', this.config.agentId, msg.from, { reason: `no handler for ${msg.type}` });
62
+ }
63
+ return handler(msg);
64
+ }
65
+ /** Advertise capabilities as a capability document */
66
+ capabilities() {
67
+ return {
68
+ agentId: this.config.agentId,
69
+ endpoint: this.config.endpoint,
70
+ capabilities: this.config.capabilities,
71
+ messageTypes: [...this.handlers.keys()],
72
+ };
73
+ }
74
+ }
75
+ /**
76
+ * ACP client - sends messages to servers and awaits responses.
77
+ */
78
+ export class AcpClient {
79
+ config;
80
+ constructor(config) {
81
+ this.config = config;
82
+ }
83
+ async send(to, type, payload) {
84
+ const server = this.config.servers.get(to);
85
+ if (!server) {
86
+ throw new Error(`unknown server: ${to}`);
87
+ }
88
+ const msg = createMessage(type, this.config.agentId, to, payload);
89
+ // In a real implementation this would make an HTTP/WebSocket call
90
+ // For now, return the message as sent (for testing)
91
+ return msg;
92
+ }
93
+ /** Discover servers by capability */
94
+ discoverByCapability(capability) {
95
+ return [...this.config.servers.values()].filter(s => s.capabilities.includes(capability));
96
+ }
97
+ }
package/dist/index.d.ts CHANGED
@@ -7,3 +7,5 @@ export { strings, type Locale, type Strings } from './i18n.ts';
7
7
  export { makeSpawnTool, MAX_SPAWN_DEPTH, type SpawnBase } from './spawn.ts';
8
8
  export { buildRegistry, contextPack, makeApproval, nativeRegistry, runAgentTask, spawnBase, toServerConfig, type AgentTaskOptions, type RunnerEvents, } from './runner.ts';
9
9
  export * from './skills-conformance.ts';
10
+ export * from './acp.ts';
11
+ export * from './topology.ts';
package/dist/index.js CHANGED
@@ -7,3 +7,5 @@ export { strings } from "./i18n.js";
7
7
  export { makeSpawnTool, MAX_SPAWN_DEPTH } from "./spawn.js";
8
8
  export { buildRegistry, contextPack, makeApproval, nativeRegistry, runAgentTask, spawnBase, toServerConfig, } from "./runner.js";
9
9
  export * from "./skills-conformance.js";
10
+ export * from "./acp.js";
11
+ export * from "./topology.js";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @hmharness/agent - Dynamic Agent Topology (P2-03)
3
+ *
4
+ * The audit called for: "根据任务复杂度自动决定单 Agent、双 Agent 或完整 Team"
5
+ *
6
+ * Analyzes task characteristics and decides the optimal agent configuration.
7
+ */
8
+ export type TopologyMode = 'single' | 'dual' | 'team' | 'pipeline';
9
+ export interface TopologyDecision {
10
+ mode: TopologyMode;
11
+ /** roles to activate */
12
+ roles: string[];
13
+ /** confidence in this decision [0,1] */
14
+ confidence: number;
15
+ /** reasoning for the decision */
16
+ reason: string;
17
+ /** estimated complexity score [0,100] */
18
+ complexity: number;
19
+ }
20
+ export interface TaskCharacteristics {
21
+ /** word count of the task description */
22
+ wordCount: number;
23
+ /** number of distinct file types mentioned */
24
+ fileTypes: number;
25
+ /** whether the task mentions building/testing/deploying */
26
+ requiresBuild: boolean;
27
+ requiresTest: boolean;
28
+ requiresDeploy: boolean;
29
+ /** whether the task mentions multiple modules/services */
30
+ multiModule: boolean;
31
+ /** estimated number of steps from the task description */
32
+ estimatedSteps: number;
33
+ /** whether the task involves external APIs/services */
34
+ externalDependencies: boolean;
35
+ }
36
+ /**
37
+ * Compute a complexity score [0,100] from task characteristics.
38
+ * Pure - testable.
39
+ */
40
+ export declare function complexityScore(c: TaskCharacteristics): number;
41
+ /**
42
+ * Decide the optimal topology based on complexity.
43
+ * Pure - testable.
44
+ */
45
+ export declare function decideTopology(c: TaskCharacteristics): TopologyDecision;
46
+ /**
47
+ * Extract task characteristics from a natural language task description.
48
+ * Pure - testable.
49
+ */
50
+ export declare function extractCharacteristics(taskDescription: string): TaskCharacteristics;
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @hmharness/agent - Dynamic Agent Topology (P2-03)
3
+ *
4
+ * The audit called for: "根据任务复杂度自动决定单 Agent、双 Agent 或完整 Team"
5
+ *
6
+ * Analyzes task characteristics and decides the optimal agent configuration.
7
+ */
8
+ /**
9
+ * Compute a complexity score [0,100] from task characteristics.
10
+ * Pure - testable.
11
+ */
12
+ export function complexityScore(c) {
13
+ let score = 0;
14
+ // word count: longer tasks are generally more complex (up to +20)
15
+ score += Math.min(20, c.wordCount / 5);
16
+ // file types: more types = more complexity (up to +15)
17
+ score += Math.min(15, c.fileTypes * 3);
18
+ // build/test/deploy requirements (+5 each, max +15)
19
+ if (c.requiresBuild)
20
+ score += 5;
21
+ if (c.requiresTest)
22
+ score += 5;
23
+ if (c.requiresDeploy)
24
+ score += 5;
25
+ // multi-module (+10)
26
+ if (c.multiModule)
27
+ score += 10;
28
+ // estimated steps (up to +25)
29
+ score += Math.min(25, c.estimatedSteps * 3);
30
+ // external deps (+15)
31
+ if (c.externalDependencies)
32
+ score += 15;
33
+ return Math.min(100, Math.round(score));
34
+ }
35
+ /**
36
+ * Decide the optimal topology based on complexity.
37
+ * Pure - testable.
38
+ */
39
+ export function decideTopology(c) {
40
+ const complexity = complexityScore(c);
41
+ if (complexity < 20) {
42
+ return {
43
+ mode: 'single', roles: ['coder'],
44
+ confidence: 0.9,
45
+ reason: `low complexity (${complexity}/100) - single agent sufficient`,
46
+ complexity,
47
+ };
48
+ }
49
+ if (complexity < 45) {
50
+ return {
51
+ mode: 'dual', roles: ['coder', 'reviewer'],
52
+ confidence: 0.8,
53
+ reason: `medium complexity (${complexity}/100) - coder + reviewer pair`,
54
+ complexity,
55
+ };
56
+ }
57
+ if (complexity < 70) {
58
+ return {
59
+ mode: 'pipeline', roles: ['planner', 'coder', 'tester', 'judge'],
60
+ confidence: 0.75,
61
+ reason: `high complexity (${complexity}/100) - pipeline with review gate`,
62
+ complexity,
63
+ };
64
+ }
65
+ return {
66
+ mode: 'team', roles: ['planner', 'architect', 'coder', 'tester', 'reviewer', 'judge'],
67
+ confidence: 0.85,
68
+ reason: `very high complexity (${complexity}/100) - full team with architecture`,
69
+ complexity,
70
+ };
71
+ }
72
+ /**
73
+ * Extract task characteristics from a natural language task description.
74
+ * Pure - testable.
75
+ */
76
+ export function extractCharacteristics(taskDescription) {
77
+ const words = taskDescription.split(/\s+/).filter(Boolean);
78
+ const lower = taskDescription.toLowerCase();
79
+ return {
80
+ wordCount: words.length,
81
+ fileTypes: countFileTypes(taskDescription),
82
+ requiresBuild: /build|compile|make|npm run/i.test(taskDescription),
83
+ requiresTest: /test|spec|verify|assert/i.test(taskDescription),
84
+ requiresDeploy: /deploy|install|publish|release/i.test(taskDescription),
85
+ multiModule: /module|service|package|component/i.test(lower) && (lower.match(/module|service|package|component/g) ?? []).length > 1,
86
+ estimatedSteps: countSteps(taskDescription),
87
+ externalDependencies: /api|http|url|fetch|request|external/i.test(lower),
88
+ };
89
+ }
90
+ function countFileTypes(text) {
91
+ const types = new Set();
92
+ const exts = text.match(/\.\w{1,5}\b/g) ?? [];
93
+ for (const e of exts)
94
+ types.add(e);
95
+ // also count mentioned languages
96
+ if (/typescript|\.ts\b/i.test(text))
97
+ types.add('.ts');
98
+ if (/javascript|\.js\b/i.test(text))
99
+ types.add('.js');
100
+ if (/python|\.py\b/i.test(text))
101
+ types.add('.py');
102
+ if (/json/i.test(text))
103
+ types.add('.json');
104
+ return types.size;
105
+ }
106
+ function countSteps(text) {
107
+ const stepPatterns = [
108
+ /first.*then/i, /step \d/i, /\d+\./, /after that/i, /next/i, /finally/i,
109
+ /then/i, /create.*write.*test/i, /read.*modify.*verify/i,
110
+ ];
111
+ let count = 1; // at least one step
112
+ for (const p of stepPatterns) {
113
+ if (p.test(text))
114
+ count++;
115
+ }
116
+ return Math.min(count, 10);
117
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hmharness/agent",
3
- "version": "0.14.16",
3
+ "version": "0.14.17",
4
4
  "description": "hmharness agent execution layer: base tools, system prompt, sub-agent spawn, and the shared task runner that frontends (cli, web) drive.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",